add preferences dialog
[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 rfbBool VncClientThread::newclient(rfbClient *cl)
32 {
33     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
34     Q_ASSERT(t);
35
36     const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
37     const int size = width * height * (depth / 8);
38     if (t->frameBuffer)
39         delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
40     t->frameBuffer = new uint8_t[size];
41     cl->frameBuffer = t->frameBuffer;
42     memset(cl->frameBuffer, '\0', size);
43     cl->format.bitsPerPixel = 32;
44     cl->format.redShift = 16;
45     cl->format.greenShift = 8;
46     cl->format.blueShift = 0;
47     cl->format.redMax = 0xff;
48     cl->format.greenMax = 0xff;
49     cl->format.blueMax = 0xff;
50
51     switch (t->quality()) {
52     case RemoteView::High:
53         cl->appData.useBGR233 = 0;
54         cl->appData.encodingsString = "copyrect hextile raw";
55         cl->appData.compressLevel = 0;
56         cl->appData.qualityLevel = 9;
57         break;
58     case RemoteView::Medium:
59         cl->appData.useBGR233 = 0;
60         cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
61         cl->appData.compressLevel = 5;
62         cl->appData.qualityLevel = 7;
63         break;
64     case RemoteView::Low:
65     case RemoteView::Unknown:
66     default:
67         cl->appData.useBGR233 = 1;
68         cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
69         cl->appData.compressLevel = 9;
70         cl->appData.qualityLevel = 1;
71     }
72
73     SetFormatAndEncodings(cl);
74
75     return true;
76 }
77
78 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
79 {
80 //     kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
81
82     const int width = cl->width, height = cl->height;
83
84     const QImage img(cl->frameBuffer, width, height, QImage::Format_RGB32);
85
86     if (img.isNull()) {
87         kDebug(5011) << "image not loaded";
88     }
89
90     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
91     Q_ASSERT(t);
92
93     t->setImage(img);
94
95     t->emitUpdated(x, y, w, h);
96 }
97
98 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
99 {
100     const QString cutText = QString::fromUtf8(text, textlen);
101     kDebug(5011) << cutText;
102
103     if (!cutText.isEmpty()) {
104         VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
105         Q_ASSERT(t);
106
107         t->emitGotCut(cutText);
108     }
109 }
110
111 char *VncClientThread::passwdHandler(rfbClient *cl)
112 {
113     kDebug(5011) << "password request" << kBacktrace();
114
115     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
116     Q_ASSERT(t);
117
118     t->passwordRequest();
119     t->m_passwordError = true;
120
121     return strdup(t->password().toLocal8Bit());
122 }
123
124 void VncClientThread::outputHandler(const char *format, ...)
125 {
126     va_list args;
127     va_start(args, format);
128
129     QString message;
130     message.vsprintf(format, args);
131
132     va_end(args);
133
134     message = message.trimmed();
135
136     kDebug(5011) << message;
137
138     if ((message.contains("Couldn't convert ")) ||
139             (message.contains("Unable to connect to VNC server")))
140         outputErrorMessageString = i18n("Server not found.");
141
142     if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
143             (message.contains("VNC connection failed: Too many authentication failures")))
144         outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
145
146     if (message.contains("VNC connection failed: Authentication failed"))
147         outputErrorMessageString = i18n("VNC authentication failed.");
148
149     if (message.contains("VNC server closed connection"))
150         outputErrorMessageString = i18n("VNC server closed connection.");
151
152     // internal messages, not displayed to user
153     if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
154         outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY";
155 }
156
157 VncClientThread::VncClientThread(QObject *parent)
158         : QThread(parent)
159         , frameBuffer(0)
160 {
161     QMutexLocker locker(&mutex);
162     m_stopped = false;
163
164     QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
165     outputErrorMessagesCheckTimer->setInterval(500);
166     connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
167     outputErrorMessagesCheckTimer->start();
168 }
169
170 VncClientThread::~VncClientThread()
171 {
172     stop();
173
174     const bool quitSuccess = wait(500);
175
176     kDebug(5011) << "Quit VNC thread success:" << quitSuccess;
177     
178     delete [] frameBuffer;
179     delete cl;
180 }
181
182 void VncClientThread::checkOutputErrorMessage()
183 {
184     if (!outputErrorMessageString.isEmpty()) {
185         kDebug(5011) << outputErrorMessageString;
186         QString errorMessage = outputErrorMessageString;
187         outputErrorMessageString.clear();
188         // show authentication failure error only after the 3rd unsuccessful try
189         if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
190             outputErrorMessage(errorMessage);
191     }
192 }
193
194 void VncClientThread::setHost(const QString &host)
195 {
196     QMutexLocker locker(&mutex);
197     m_host = host;
198 }
199
200 void VncClientThread::setPort(int port)
201 {
202     QMutexLocker locker(&mutex);
203     m_port = port;
204 }
205
206 void VncClientThread::setQuality(RemoteView::Quality quality)
207 {
208     m_quality = quality;
209 }
210
211 RemoteView::Quality VncClientThread::quality() const
212 {
213     return m_quality;
214 }
215
216 void VncClientThread::setImage(const QImage &img)
217 {
218     QMutexLocker locker(&mutex);
219     m_image = img;
220 }
221
222 const QImage VncClientThread::image(int x, int y, int w, int h)
223 {
224     QMutexLocker locker(&mutex);
225
226     if (w == 0) // full image requested
227         return m_image;
228     else
229         return m_image.copy(x, y, w, h);
230 }
231
232 void VncClientThread::emitUpdated(int x, int y, int w, int h)
233 {
234     emit imageUpdated(x, y, w, h);
235 }
236
237 void VncClientThread::emitGotCut(const QString &text)
238 {
239     emit gotCut(text);
240 }
241
242 void VncClientThread::stop()
243 {
244     QMutexLocker locker(&mutex);
245     m_stopped = true;
246 }
247
248 void VncClientThread::run()
249 {
250     QMutexLocker locker(&mutex);
251
252     while (!m_stopped) { // try to connect as long as the server allows
253         m_passwordError = false;
254
255         rfbClientLog = outputHandler;
256         rfbClientErr = outputHandler;
257         cl = rfbGetClient(8, 3, 4);
258         cl->MallocFrameBuffer = newclient;
259         cl->canHandleNewFBSize = true;
260         cl->GetPassword = passwdHandler;
261         cl->GotFrameBufferUpdate = updatefb;
262         cl->GotXCutText = cuttext;
263         rfbClientSetClientData(cl, 0, this);
264
265         cl->serverHost = strdup(m_host.toUtf8().constData());
266
267         if (m_port < 0 || !m_port) // port is invalid or empty...
268             m_port = 5900; // fallback: try an often used VNC port
269
270         if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
271             m_port += 5900;
272         cl->serverPort = m_port;
273
274         kDebug(5011) << "--------------------- trying init ---------------------";
275
276         if (rfbInitClient(cl, 0, 0))
277             break;
278
279         if (m_passwordError)
280             continue;
281
282         return;
283     }
284
285     locker.unlock();
286
287     // Main VNC event loop
288     while (!m_stopped) {
289         const int i = WaitForMessage(cl, 500);
290         if (i < 0)
291             break;
292         if (i)
293             if (!HandleRFBServerMessage(cl))
294                 break;
295
296         locker.relock();
297
298         while (!m_eventQueue.isEmpty()) {
299             ClientEvent* clientEvent = m_eventQueue.dequeue();
300             clientEvent->fire(cl);
301             delete clientEvent;
302         }
303
304         locker.unlock();
305     }
306
307     // Cleanup allocated resources
308     locker.relock();
309     rfbClientCleanup(cl);
310     m_stopped = true;
311 }
312
313 ClientEvent::~ClientEvent()
314 {
315 }
316
317 void PointerClientEvent::fire(rfbClient* cl)
318 {
319     SendPointerEvent(cl, m_x, m_y, m_buttonMask);
320 }
321
322 void KeyClientEvent::fire(rfbClient* cl)
323 {
324     SendKeyEvent(cl, m_key, m_pressed);
325 }
326
327 void ClientCutEvent::fire(rfbClient* cl)
328 {
329     SendClientCutText(cl, text.toUtf8().data(), text.size());
330 }
331
332 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
333 {
334     QMutexLocker lock(&mutex);
335     if (m_stopped)
336         return;
337
338     m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
339 }
340
341 void VncClientThread::keyEvent(int key, bool pressed)
342 {
343     QMutexLocker lock(&mutex);
344     if (m_stopped)
345         return;
346
347     m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
348 }
349
350 void VncClientThread::clientCut(const QString &text)
351 {
352     QMutexLocker lock(&mutex);
353     if (m_stopped)
354         return;
355
356     m_eventQueue.enqueue(new ClientCutEvent(text));
357 }
358
359 #include "moc_vncclientthread.cpp"