summaryrefslogblamecommitdiffstats
path: root/src/util/vncClientThread.cpp
blob: bb1d457512f29f1e9ce1c1ef5426e21caaac02f1 (plain) (tree)
























































































































































































































































                                                                                          
/*
 # 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 "vncClientThread.h"

VNCClientThread::VNCClientThread(QString host, int port, QString passwd,
                                 int quality, int updatefreq) :
        QThread(), _frameBuffer(0)
{
    _host = host;
    _port = port;
    _passwd = passwd;
    _quality = quality;
    _updatefreq = updatefreq;
    terminate = false;
    _client = NULL;
    _connected = false;
}

VNCClientThread::~VNCClientThread()
{
	if (this->isRunning()) this->wait(2000);
    if (_frameBuffer) delete[] _frameBuffer;
    _frameBuffer = NULL;
    if (_client != NULL)
    {
    	::close(_client->sock);
    	_client->frameBuffer = NULL;
    	rfbClientCleanup(_client);
    	_client = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////
// Public

void VNCClientThread::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;
    _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
        return; // later when you try to clean up _client, as rfbInitClient already did so
    }

    qDebug("[%s] Connection successful!", metaObject()->className());

    // Main VNC event loop
    while (!terminate)
    {
    	_connected = true;
        const int i = WaitForMessage(_client, 500); // its usec, not msec
        if (i < 0)
            break;
        if (i) if (!HandleRFBServerMessage(_client))
            break;

        /*
        //work yourself through event queue and fire every event...
        while (!_eventQueue.isEmpty()) {
                    SomeEvent* event = _eventQueue.dequeue();
                    event->fire(_client);
                    delete event;
        }*/

        this->msleep(_updatefreq);
    }

    // cleanup
    ::close(_client->sock);

    qDebug("[%s] VNC client stopped.", metaObject()->className());
    _connected = false;
}

QImage VNCClientThread::getImage()
{
    return _img;
}

QSize VNCClientThread::getSize()
{
    return _clientSize;
}

int VNCClientThread::getUpdatefreq()
{
	if (_updatefreq > 0)
		return _updatefreq;
	return 500;
}

void VNCClientThread::setUpdatefreq(int updatefreq)
{
	_updatefreq = updatefreq;
}

QString VNCClientThread::getDesktopName()
{
	if (_client == NULL || _client->desktopName == NULL) return QString();
	return QString(_client->desktopName);
}

char* VNCClientThread::passwdHandler(rfbClient *client)
{
    VNCClientThread* t = (VNCClientThread*) rfbClientGetClientData(client, 0);
    return strdup(t->_passwd.toLocal8Bit());
}

rfbBool VNCClientThread::frameBufferHandler(rfbClient *client)
{
    VNCClientThread *t = (VNCClientThread*) 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);

    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 VNCClientThread::HIGH:
        client->appData.useBGR233 = 0;
        client->appData.encodingsString = "copyrect hextile raw";
        client->appData.compressLevel = 0;
        client->appData.qualityLevel = 9;
        client->appData.scaleSetting = 10; // FIXME: Doesn't work
        break;
    case VNCClientThread::MEDIUM:
        client->appData.useBGR233 = 0;
        client->appData.encodingsString
        = "tight zrle ultra copyrect hextile zlib corre rre raw";
        client->appData.compressLevel = 5;
        client->appData.qualityLevel = 7;
        client->appData.scaleSetting = 10;
        break;
    case VNCClientThread::LOW:
    default:
        client->appData.useBGR233 = 1;
        client->appData.encodingsString
        = "tight zrle ultra copyrect hextile zlib corre rre raw";
        client->appData.compressLevel = 9;
        client->appData.qualityLevel = 1;
        client->appData.scaleSetting = 10;
        break;
    }
    SetFormatAndEncodings(client);

    t->_clientSize = QSize(width, height);

    // If something stops working with VNC images/updates, move these two
    // commands back tp updateImage
	const QImage img = QImage(client->frameBuffer, client->width,
						  client->height, QImage::Format_RGB32);
	t->_img = img;
	// <>

    return true;
}

void VNCClientThread::updateImage(rfbClient* client, int x, int y, int w, int h)
{
    VNCClientThread* t = (VNCClientThread*) rfbClientGetClientData(client, 0);
    emit t->imageUpdated(x, y, w, h);
}

/* rfbClient* VNCClientThread::getRfbClient(){
    return _client;
}*/

SomeEvent::~SomeEvent()
{
}

void PointerEvent::fire(rfbClient* cl)
{
    SendPointerEvent(cl, _x, _y, _buttonMask);
}

void KeyEvent::fire(rfbClient* cl)
{
    SendKeyEvent(cl, _key, _pressed);
}

void VNCClientThread::mouseEvent(int x, int y, int buttonMask)
{
    //QMutexLocker lock(&mutex);
    if (terminate)
        return;

    _eventQueue.enqueue(new PointerEvent(x, y, buttonMask));
}

void VNCClientThread::keyEvent(int key, bool pressed)
{
    //QMutexLocker lock(&mutex);
    if (terminate)
        return;

    _eventQueue.enqueue(new KeyEvent(key, pressed));
}