bump version
[presencevnc] / src / vncclientthread.cpp
index e215b3d..9f80c6f 100644 (file)
 
 #include "vncclientthread.h"
 
-#include <QCoreApplication>
 #include <QMutexLocker>
 #include <QTimer>
 
 static QString outputErrorMessageString;
 
+#ifdef Q_WS_MAEMO_5
+//N900 display has 16bit depth (R/G/B with 5/6/5 bpp)
+const int MAX_COLOR_DEPTH = 16;
+#else
+const int MAX_COLOR_DEPTH = 32;
+#endif
+
+
 rfbBool VncClientThread::newclient(rfbClient *cl)
 {
-    VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+    VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
     Q_ASSERT(t);
 
-    const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
-    const int size = width * height * (depth / 8);
-    if (t->frameBuffer)
-        delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
-    t->frameBuffer = new uint8_t[size];
-    cl->frameBuffer = t->frameBuffer;
-    memset(cl->frameBuffer, '\0', size);
-    cl->format.bitsPerPixel = 32;
-    cl->format.redShift = 16;
-    cl->format.greenShift = 8;
-    cl->format.blueShift = 0;
-    cl->format.redMax = 0xff;
-    cl->format.greenMax = 0xff;
-    cl->format.blueMax = 0xff;
-
     switch (t->quality()) {
     case RemoteView::High:
+        cl->format.bitsPerPixel = MAX_COLOR_DEPTH;
         cl->appData.useBGR233 = 0;
         cl->appData.encodingsString = "copyrect hextile raw";
         cl->appData.compressLevel = 0;
         cl->appData.qualityLevel = 9;
         break;
     case RemoteView::Medium:
+        cl->format.bitsPerPixel = 16;
         cl->appData.useBGR233 = 0;
         cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
         cl->appData.compressLevel = 5;
@@ -65,12 +59,37 @@ rfbBool VncClientThread::newclient(rfbClient *cl)
     case RemoteView::Low:
     case RemoteView::Unknown:
     default:
-        cl->appData.useBGR233 = 1;
+        cl->format.bitsPerPixel = 16; //TODO: add support for 8bit (needs color map)
         cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
         cl->appData.compressLevel = 9;
         cl->appData.qualityLevel = 1;
     }
 
+    if(cl->format.bitsPerPixel == 16) {
+        cl->format.depth = 16; //number of useful bits in the pixel value
+        cl->format.redShift = 11;
+        cl->format.greenShift = 5;
+        cl->format.blueShift = 0;
+        cl->format.redMax = 0x1f;
+        cl->format.greenMax = 0x3f;
+        cl->format.blueMax = 0x1f;
+    } else {
+        cl->format.depth = 24; //number of useful bits in the pixel value
+        cl->format.redShift = 16;
+        cl->format.greenShift = 8;
+        cl->format.blueShift = 0;
+        cl->format.redMax = 0xff;
+        cl->format.greenMax = 0xff;
+        cl->format.blueMax = 0xff;
+    }
+
+    delete [] t->m_frameBuffer; // do not leak if we get a new framebuffer size
+    const int size = cl->width * cl->height * (cl->format.bitsPerPixel / 8);
+    t->m_frameBuffer = new uint8_t[size];
+    cl->frameBuffer = t->m_frameBuffer;
+    memset(cl->frameBuffer, '\0', size);
+
+
     SetFormatAndEncodings(cl);
 
     return true;
@@ -78,17 +97,20 @@ rfbBool VncClientThread::newclient(rfbClient *cl)
 
 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
 {
-     //kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
+    //kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
 
-    const int width = cl->width, height = cl->height;
-
-    const QImage img(cl->frameBuffer, width, height, QImage::Format_RGB32);
+    const QImage img(
+            cl->frameBuffer,
+            cl->width,
+            cl->height,
+            (cl->format.bitsPerPixel==16)?QImage::Format_RGB16:QImage::Format_RGB32
+    );
 
     if (img.isNull()) {
         kDebug(5011) << "image not loaded";
     }
 
-    VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+    VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
     Q_ASSERT(t);
 
     t->setImage(img);
@@ -102,7 +124,7 @@ void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
     kDebug(5011) << "cuttext: " << cutText;
 
     if (!cutText.isEmpty()) {
-        VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+        VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
         Q_ASSERT(t);
 
         t->emitGotCut(cutText);
@@ -113,15 +135,23 @@ char *VncClientThread::passwdHandler(rfbClient *cl)
 {
     kDebug(5011) << "password request" << kBacktrace();
 
-    VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
+    VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
     Q_ASSERT(t);
 
-    t->passwordRequest();
     t->m_passwordError = true;
+    t->passwordRequest();
 
     return strdup(t->password().toLocal8Bit());
 }
 
+void VncClientThread::setPassword(const QString &password)
+{
+    if(password.isNull()) //cancelled, don't retry
+        m_passwordError = false;
+
+    m_password = password;
+}
+
 void VncClientThread::outputHandler(const char *format, ...)
 {
     va_list args;
@@ -139,27 +169,22 @@ void VncClientThread::outputHandler(const char *format, ...)
     if ((message.contains("Couldn't convert ")) ||
             (message.contains("Unable to connect to VNC server")))
         outputErrorMessageString = i18n("Server not found.");
-
-    if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
+    else if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
             (message.contains("VNC connection failed: Too many authentication failures")))
         outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
-
-    if (message.contains("VNC connection failed: Authentication failed"))
+    else if (message.contains("VNC connection failed: Authentication failed"))
         outputErrorMessageString = i18n("VNC authentication failed.");
-
-    if (message.contains("VNC server closed connection"))
+    else if (message.contains("VNC server closed connection"))
         outputErrorMessageString = i18n("VNC server closed connection.");
-
-    // internal messages, not displayed to user
-    if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
-        outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY";
+    else if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
+        outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY"; // internal messages, not displayed to user
 }
 
 VncClientThread::VncClientThread(QObject *parent)
-        : QThread(parent)
-        , frameBuffer(0)
+    : QThread(parent)
+    , m_frameBuffer(0)
 {
-    QMutexLocker locker(&mutex);
+    QMutexLocker locker(&m_mutex);
     m_stopped = false;
 
     QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
@@ -172,36 +197,35 @@ VncClientThread::~VncClientThread()
 {
     stop();
 
-    const bool quitSuccess = wait(1000);
+    const bool quitSuccess = wait(4000);
 
-       if(!quitSuccess)
-               kDebug(5011) << "~VncClientThread(): Quit failed";
-    
-    delete [] frameBuffer;
-    //cl is free()d when event loop exits.
+    if(!quitSuccess)
+        kDebug(5011) << "~VncClientThread(): Quit failed";
+
+    delete [] m_frameBuffer;
+    //m_cl is free()d when event loop exits.
 }
 
 void VncClientThread::checkOutputErrorMessage()
 {
     if (!outputErrorMessageString.isEmpty()) {
-        kDebug(5011) << outputErrorMessageString;
         QString errorMessage = outputErrorMessageString;
         outputErrorMessageString.clear();
         // show authentication failure error only after the 3rd unsuccessful try
         if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
-            outputErrorMessage(errorMessage);
+            emit outputErrorMessage(errorMessage);
     }
 }
 
 void VncClientThread::setHost(const QString &host)
 {
-    QMutexLocker locker(&mutex);
+    QMutexLocker locker(&m_mutex);
     m_host = host;
 }
 
 void VncClientThread::setPort(int port)
 {
-    QMutexLocker locker(&mutex);
+    QMutexLocker locker(&m_mutex);
     m_port = port;
 }
 
@@ -217,13 +241,13 @@ RemoteView::Quality VncClientThread::quality() const
 
 void VncClientThread::setImage(const QImage &img)
 {
-    QMutexLocker locker(&mutex);
+    QMutexLocker locker(&m_mutex);
     m_image = img;
 }
 
 const QImage VncClientThread::image(int x, int y, int w, int h)
 {
-    QMutexLocker locker(&mutex);
+    QMutexLocker locker(&m_mutex);
 
     if (w == 0) // full image requested
         return m_image;
@@ -243,81 +267,82 @@ void VncClientThread::emitGotCut(const QString &text)
 
 void VncClientThread::stop()
 {
-    QMutexLocker locker(&mutex);
+    if(m_stopped)
+        return;
 
+    //also abort listening for connections, should be safe without locking
+    if(m_listen_port)
+        m_cl->listenSpecified = false;
+
+    QMutexLocker locker(&m_mutex);
     m_stopped = true;
 }
 
 void VncClientThread::run()
 {
-    QMutexLocker locker(&mutex);
-       bool clean = false;
+    QMutexLocker locker(&m_mutex);
 
     int passwd_failures = 0;
     while (!m_stopped) { // try to connect as long as the server allows
         m_passwordError = false;
+        outputErrorMessageString.clear(); //don't deliver error messages of old instances...
 
         rfbClientLog = outputHandler;
         rfbClientErr = outputHandler;
-        cl = rfbGetClient(8, 3, 4);
-        cl->MallocFrameBuffer = newclient;
-        cl->canHandleNewFBSize = true;
-        cl->GetPassword = passwdHandler;
-        cl->GotFrameBufferUpdate = updatefb;
-        cl->GotXCutText = cuttext;
-        rfbClientSetClientData(cl, 0, this);
+        m_cl = rfbGetClient(8, 3, 4); // bitsPerSample, samplesPerPixel, bytesPerPixel
+        m_cl->MallocFrameBuffer = newclient;
+        m_cl->canHandleNewFBSize = true;
+        m_cl->GetPassword = passwdHandler;
+        m_cl->GotFrameBufferUpdate = updatefb;
+        m_cl->GotXCutText = cuttext;
+        rfbClientSetClientData(m_cl, 0, this);
 
-        cl->serverHost = strdup(m_host.toUtf8().constData());
+        m_cl->serverHost = strdup(m_host.toUtf8().constData());
 
         if (m_port < 0 || !m_port) // port is invalid or empty...
             m_port = 5900; // fallback: try an often used VNC port
 
         if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
             m_port += 5900;
-        cl->serverPort = m_port;
+        m_cl->serverPort = m_port;
+
+        m_cl->listenSpecified = rfbBool(m_listen_port > 0);
+        m_cl->listenPort = m_listen_port;
 
         kDebug(5011) << "--------------------- trying init ---------------------";
 
-               if(listen_port) { //listen for incoming connections
-                       int argc = 2;
-                       char* argv[2] = { "x", "-listen" }; //this isn't exactly elegant..
-                       cl->listenPort = listen_port;
-                       if (rfbInitClient(cl, &argc, argv))
-                               break;
-               } else { //connect to host
-                       if (rfbInitClient(cl, 0, 0))
-                               break;
-               }
+        if (rfbInitClient(m_cl, 0, 0))
+            break;
 
+        //init failed...
         if (m_passwordError) {
-                       passwd_failures++;
-                       if(passwd_failures > 2) {
-                               m_stopped = true;
-                               clean = true; //rfbInitClient cleans up after itself upon failure
-                       }
-                       continue;
-               }
+            passwd_failures++;
+            if(passwd_failures < 3)
+                continue; //that's ok, try again
+        }
 
-        return;
+        //stop connecting
+        m_stopped = true;
+        return; //no cleanup necessary, m_cl was free()d by rfbInitClient()
     }
 
     locker.unlock();
 
     // Main VNC event loop
     while (!m_stopped) {
-        const int i = WaitForMessage(cl, 500);
-               if(m_stopped or i < 0)
-                       break;
+        const int i = WaitForMessage(m_cl, 500);
+        if(m_stopped or i < 0)
+            break;
 
         if (i)
-            if (!HandleRFBServerMessage(cl))
+            if (!HandleRFBServerMessage(m_cl))
                 break;
 
         locker.relock();
 
         while (!m_eventQueue.isEmpty()) {
             ClientEvent* clientEvent = m_eventQueue.dequeue();
-            clientEvent->fire(cl);
+            clientEvent->fire(m_cl);
             delete clientEvent;
         }
 
@@ -326,8 +351,7 @@ void VncClientThread::run()
 
     // Cleanup allocated resources
     locker.relock();
-       if(!clean)
-               rfbClientCleanup(cl);
+    rfbClientCleanup(m_cl);
     m_stopped = true;
 }
 
@@ -352,7 +376,7 @@ void ClientCutEvent::fire(rfbClient* cl)
 
 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
 {
-    QMutexLocker lock(&mutex);
+    QMutexLocker lock(&m_mutex);
     if (m_stopped)
         return;
 
@@ -361,7 +385,7 @@ void VncClientThread::mouseEvent(int x, int y, int buttonMask)
 
 void VncClientThread::keyEvent(int key, bool pressed)
 {
-    QMutexLocker lock(&mutex);
+    QMutexLocker lock(&m_mutex);
     if (m_stopped)
         return;
 
@@ -370,11 +394,9 @@ void VncClientThread::keyEvent(int key, bool pressed)
 
 void VncClientThread::clientCut(const QString &text)
 {
-    QMutexLocker lock(&mutex);
+    QMutexLocker lock(&m_mutex);
     if (m_stopped)
         return;
 
     m_eventQueue.enqueue(new ClientCutEvent(text));
 }
-
-#include "moc_vncclientthread.cpp"