summaryrefslogtreecommitdiffstats
path: root/src/client/vnc/vncthread.cpp
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/client/vnc/vncthread.cpp
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/client/vnc/vncthread.cpp')
-rw-r--r--src/client/vnc/vncthread.cpp314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/client/vnc/vncthread.cpp b/src/client/vnc/vncthread.cpp
new file mode 100644
index 0000000..492f970
--- /dev/null
+++ b/src/client/vnc/vncthread.cpp
@@ -0,0 +1,314 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # vncClientThread.cpp
+ # - Connection to remove vnc server
+ # - Emits Qt signal on framebuffer updates
+ # -----------------------------------------------------------------------------
+ */
+
+#include "vncthread.h"
+#include <QPainter>
+
+#include <netinet/tcp.h>
+
+// greatest common divisor
+static int gcd(int a, int b)
+{
+ if (b == 0)
+ return a;
+ return gcd(b, a % b);
+}
+
+VncThread::VncThread(QString host, int port, QString passwd, int quality) :
+ QThread(), _frameBuffer(NULL), _painter(NULL), _hasNewLocalSize(false), _run(true)
+{
+ _srcStepX = _srcStepY = _dstStepX = _dstStepY = 0;
+ _host = host;
+ _port = port;
+ _passwd = passwd;
+ _quality = quality;
+ _client = NULL;
+ _connected = false;
+ moveToThread(this);
+}
+
+// ALWAYS delete this class from another thread using delete, not deleteLater, or you will deadlock the thread
+VncThread::~VncThread()
+{
+ qDebug("VNC worker destructor called, waiting for thread finishing...");
+ _run = false;
+ while (this->isRunning())
+ this->msleep(10);
+ qDebug("Thread ended.");
+ if (_frameBuffer)
+ delete[] _frameBuffer;
+ if (_client != NULL)
+ {
+ if (_client->sock != -1)
+ ::close(_client->sock);
+ _client->frameBuffer = NULL;
+ rfbClientCleanup(_client);
+ }
+ if (_painter != NULL)
+ delete _painter;
+}
+
+// Calc matching pixel borders for both resolutions to prevent artifacts through bilinear scaling
+void VncThread::calcScaling()
+{
+ if (_localSize.isEmpty() || _localSize.width() == 0 || _localSize.height() == 0)
+ return;
+ if (_clientSize.isEmpty() || _clientSize.width() == 0 || _clientSize.height() == 0)
+ return;
+ const int gcdX = gcd(_localSize.width(), _clientSize.width());
+ const int gcdY = gcd(_localSize.height(), _clientSize.height());
+ _srcStepX = _clientSize.width() / gcdX;
+ _srcStepY = _clientSize.height() / gcdY;
+ _dstStepX = _localSize.width() / gcdX;
+ _dstStepY = _localSize.height() / gcdY;
+ qDebug() << "Scaling updated to " << _clientSize << " -> " << _localSize;
+ emit imageUpdated(0, 0, _localSize.width(), _localSize.height());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Public
+
+void VncThread::setTargetSize(const QSize size)
+{
+ if (_localSize == size)
+ return;
+ qDebug() << "Setting target size to " << size;
+ QMutexLocker lock(&_mutex);
+ _newLocalSize = size;
+ _hasNewLocalSize = true;
+}
+
+void VncThread::run()
+{
+ qDebug("[%s] VNC client started.", metaObject()->className());
+ qDebug("[%s] Host: '%s' Port: %i Passwd: '%s' Quality: %i", metaObject()->className(), qPrintable(_host), _port,
+ qPrintable(_passwd), _quality);
+
+ // setup network
+ _client = rfbGetClient(8, 3, 4);
+ _client->MallocFrameBuffer = &frameBufferHandler;
+ _client->canHandleNewFBSize = true;
+ free(_client->serverHost); // in rfbGetClient, serverHost is assigned strdup(""), so free that first.
+ _client->serverHost = strdup(_host.toUtf8().constData());
+ _client->desktopName = NULL;
+ _client->serverPort = _port;
+ _client->GetPassword = &passwdHandler;
+ _client->GotFrameBufferUpdate = &updateImage;
+ _client->frameBuffer = NULL;
+
+ // save this instance in vnc-struct for callbacks
+ rfbClientSetClientData(_client, 0, this);
+
+ // start client
+ if (!rfbInitClient(_client, NULL, NULL))
+ {
+ _client = NULL; // !!! <- if you don't do this you will get a segfault later when you try to clean up _client, as rfbInitClient already did so
+ this->stop();
+ return;
+ }
+
+ qDebug("[%s] Connection successful!", metaObject()->className());
+ int one = 1;
+ setsockopt(_client->sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
+ one = 1;
+ setsockopt(_client->sock, SOL_TCP, TCP_QUICKACK, &one, sizeof(one));
+
+ // Main VNC event loop
+ emit projectionStarted();
+ while (_run)
+ {
+ _connected = true;
+ const int i = WaitForMessage(_client, 100 * 1000); // wait 100ms for message. returns -1 on error/disconnect, 0 if nothing happened, 1 if new data arrived
+ if (i < 0)
+ break;
+ if (i > 0 && !HandleRFBServerMessage(_client))
+ break;
+
+ if (_hasNewLocalSize)
+ {
+ QMutexLocker lock(&_mutex);
+ _hasNewLocalSize = false;
+ _localSize = _newLocalSize;
+ if (_painter != NULL)
+ delete _painter;
+ _imgScaled = QImage(_localSize, QImage::Format_RGB32);
+ _painter = new QPainter(&_imgScaled);
+ this->calcScaling();
+ }
+
+ /*
+ //work yourself through event queue and fire every event...
+ while (!_eventQueue.isEmpty()) {
+ SomeEvent* event = _eventQueue.dequeue();
+ event->fire(_client);
+ delete event;
+ }*/
+ }
+
+ _connected = false;
+ qDebug("[%s] VNC client stopped.", metaObject()->className());
+}
+
+const QString VncThread::getDesktopName() const
+{
+ if (_client == NULL || _client->desktopName == NULL)
+ return QString();
+ return QString(_client->desktopName);
+}
+
+void VncThread::mouseEvent(int x, int y, int buttonMask)
+{
+ //QMutexLocker lock(&mutex);
+ if (!_run)
+ return;
+
+ _eventQueue.enqueue(new PointerEvent(x, y, buttonMask));
+}
+
+void VncThread::keyEvent(int key, bool pressed)
+{
+ //QMutexLocker lock(&mutex);
+ if (!_run)
+ return;
+
+ _eventQueue.enqueue(new KeyEvent(key, pressed));
+}
+
+void VncThread::processImageUpdate(const int x, const int y, const int w, const int h)
+{
+ if (_srcStepX > 1 || _srcStepY > 1)
+ {
+ // Scaling is required as vnc server and client are using different resolutions
+ // Calc section offsets first
+ const int startX = x / _srcStepX;
+ const int startY = y / _srcStepY;
+ const int endX = (x + w - 1) / _srcStepX + 1;
+ const int endY = (y + h - 1) / _srcStepY + 1;
+ // Now pixel offsets for source
+ const int srcX = startX * _srcStepX;
+ const int srcY = startY * _srcStepY;
+ const int srcW = endX * _srcStepX - srcX;
+ const int srcH = endY * _srcStepY - srcY;
+ // Pixel offsets for destination
+ const int dstX = startX * _dstStepX;
+ const int dstY = startY * _dstStepY;
+ const int dstW = endX * _dstStepX - dstX;
+ const int dstH = endY * _dstStepY - dstY;
+ // Rescale
+ if (_painter != NULL)
+ {
+ QImage scaled(
+ _img.copy(srcX, srcY, srcW, srcH).scaled(dstW, dstH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ _painter->drawImage(dstX, dstY, scaled, 0, 0, dstW, dstH);
+ emit imageUpdated(dstX, dstY, dstW, dstH);
+ }
+ }
+ else
+ {
+ // Same resolution, nothing to do
+ emit imageUpdated(x, y, w, h);
+ }
+}
+
+// *** callback stuff ***
+
+// the vnc lib is requesting the connection password
+char* VncThread::passwdHandler(rfbClient *client)
+{
+ VncThread* t = (VncThread*)rfbClientGetClientData(client, 0);
+ return strdup(t->_passwd.toUtf8());
+}
+
+// the vnc lib is telling us the size and bit depth of the remote screen
+rfbBool VncThread::frameBufferHandler(rfbClient *client)
+{
+ VncThread *t = (VncThread*)rfbClientGetClientData(client, 0);
+ const int width = client->width, height = client->height, depth = client->format.bitsPerPixel;
+ const int size = width * height * (depth / 8);
+ qDebug("[%s] Remote desktop: %ix%ix%i", t->metaObject()->className(), width, height, depth);
+
+ QMutexLocker lock(&(t->_mutex));
+
+ if (t->_frameBuffer)
+ delete[] t->_frameBuffer;
+
+ t->_frameBuffer = new uint8_t[size];
+ client->frameBuffer = t->_frameBuffer;
+ memset(client->frameBuffer, '\0', size);
+ client->format.bitsPerPixel = 32;
+ client->format.redShift = 16;
+ client->format.greenShift = 8;
+ client->format.blueShift = 0;
+ client->format.redMax = 0xff;
+ client->format.greenMax = 0xff;
+ client->format.blueMax = 0xff;
+
+ const int quality = t->_quality;
+ switch (quality)
+ {
+ case VncThread::HIGH:
+ client->appData.useBGR233 = 0;
+ client->appData.encodingsString = "copyrect zlib hextile raw";
+ client->appData.compressLevel = 4;
+ client->appData.qualityLevel = 9;
+ client->appData.scaleSetting = 0;
+ break;
+ case VncThread::MEDIUM:
+ client->appData.useBGR233 = 0;
+ client->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
+ client->appData.compressLevel = 7;
+ client->appData.qualityLevel = 8;
+ client->appData.scaleSetting = 0;
+ break;
+ case VncThread::LOW:
+ default:
+ client->appData.useBGR233 = 1;
+ client->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
+ client->appData.compressLevel = 9;
+ client->appData.qualityLevel = 1;
+ client->appData.scaleSetting = 0;
+ break;
+ }
+ SetFormatAndEncodings(client);
+
+ t->_clientSize = QSize(width, height);
+
+ t->_img = QImage(client->frameBuffer, client->width, client->height, QImage::Format_RGB32);
+
+ t->calcScaling();
+
+ return true;
+}
+
+void VncThread::updateImage(rfbClient* client, int x, int y, int w, int h)
+{
+ VncThread* t = (VncThread*)rfbClientGetClientData(client, 0);
+ t->processImageUpdate(x, y, w, h);
+}
+
+// *** event stuff ***
+
+void PointerEvent::fire(rfbClient* cl)
+{
+ SendPointerEvent(cl, _x, _y, _buttonMask);
+}
+
+void KeyEvent::fire(rfbClient* cl)
+{
+ SendKeyEvent(cl, _key, _pressed);
+}