summaryrefslogtreecommitdiffstats
path: root/src/client/vnc
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
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/client/vnc')
-rw-r--r--src/client/vnc/vncserver.cpp191
-rw-r--r--src/client/vnc/vncserver.h52
-rw-r--r--src/client/vnc/vncthread.cpp314
-rw-r--r--src/client/vnc/vncthread.h141
-rw-r--r--src/client/vnc/vncwindow.cpp277
-rw-r--r--src/client/vnc/vncwindow.h73
6 files changed, 1048 insertions, 0 deletions
diff --git a/src/client/vnc/vncserver.cpp b/src/client/vnc/vncserver.cpp
new file mode 100644
index 0000000..2b49b8e
--- /dev/null
+++ b/src/client/vnc/vncserver.cpp
@@ -0,0 +1,191 @@
+/*
+ * vncserver.cpp
+ *
+ * Created on: 24.01.2013
+ * Author: sr
+ */
+
+#include <QProcess>
+#include "vncserver.h"
+#include "../util/util.h"
+
+/*******************************************
+ * STATIC
+ *******************************************/
+
+VncServer* VncServer::me = NULL;
+
+VncServer* VncServer::instance()
+{
+ if (me == NULL)
+ me = new VncServer();
+ return me;
+}
+
+//
+static QString makePassword(int len = 10)
+{
+ char pass[len];
+ for (int i = 0; i < len; ++i)
+ pass[i] = 43 + qrand() % 80;
+ return QString::fromUtf8(pass, len);
+}
+
+// Ugly hack to get an el-cheapo platform independent sleep
+struct Sleeper : public QThread
+{
+static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
+};
+
+/*******************************************
+ * INSTANCE
+ *******************************************/
+
+VncServer::VncServer() :
+ _process(NULL), _port(0), _timerId(0)
+{
+ // TODO Auto-generated constructor stub
+}
+
+VncServer::~VncServer()
+{
+ // TODO Auto-generated destructor stub
+}
+
+void VncServer::start()
+{
+ // Keep things clean
+ if (_process != NULL)
+ {
+ disconnect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
+ disconnect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
+ }
+ this->stop();
+ // Generate passwords
+ _rwpass = makePassword();
+ _ropass = makePassword();
+ // Create new password file
+ QDir path = Util::settingsDir();
+ QString pwfile(path.absoluteFilePath("vncpass"));
+ QFile pwhandle(pwfile);
+ if (pwhandle.exists())
+ pwhandle.remove();
+ if (!pwhandle.open(QIODevice::WriteOnly))
+ {
+ qDebug() << "Could not open " << pwfile << " for writing";
+ emit started(0, _ropass, _rwpass);
+ return;
+ }
+ pwhandle.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
+ pwhandle.write(_rwpass.toUtf8().constData());
+ pwhandle.write("\n");
+ pwhandle.write(_ropass.toUtf8().constData());
+ pwhandle.write("\n");
+ pwhandle.close();
+ // Create new process
+ _process = new QProcess(this);
+ connect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
+ connect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
+ connect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
+ connect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
+ _timerId = startTimer(4000);
+ QStringList args;
+ args << "-forever";
+ args << "-display" << ":0";
+ args << "-passwdfile" << (QString("rm:" + pwfile));
+ args << "-shared";
+ args << "-autoport" << QString::number(54112);
+ qDebug() << "Arguments are: " << args;
+ _process->start("x11vnc",
+ args,
+ QIODevice::ReadOnly);
+}
+
+void VncServer::stop()
+{
+ if (_timerId != 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ if (_process == NULL)
+ return;
+ qDebug("Stopping old VNC server.");
+ disconnect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
+ disconnect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
+ QProcess *process = _process;
+ _process = NULL;
+ _port = 0;
+ process->terminate();
+ for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
+ Sleeper::msleep(10);
+ if (process->state() == QProcess::Running)
+ process->kill();
+ for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
+ Sleeper::msleep(10);
+ process->deleteLater();
+}
+
+/**
+ * Overrides
+ */
+
+void VncServer::timerEvent(QTimerEvent *event)
+{
+ // Error timeout (3s), tell server that vnc setup failed
+ this->stop();
+ emit started(0, _ropass, _rwpass);
+}
+
+/**
+ * Slots
+ */
+
+void VncServer::onStdOut()
+{
+ if (_process == NULL)
+ {
+ qDebug("VncServer::onStdOut() called in bad state.");
+ return;
+ }
+ QByteArray data(_process->readAllStandardOutput());
+ qDebug() << "x11vnc: " << data;
+ if (_port <= 0)
+ {
+ const int pos = data.indexOf("PORT=", 0);
+ if (pos != -1)
+ {
+ _port = atoi(data.constData() + pos + 5);
+ qDebug() << "Got VNC port " << _port << ", ro " << _ropass << ", rw " << _rwpass;
+ emit started(_port, _ropass, _rwpass);
+ // Kill error timer, but only if port seemed valid
+ if (_timerId != 0 && _port > 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ }
+ }
+}
+
+void VncServer::onStdErr()
+{
+ if (_process == NULL)
+ {
+ qDebug("VncServer::onStdErr() called in bad state.");
+ return;
+ }
+ QByteArray data(_process->readAllStandardError());
+}
+
+void VncServer::onError(QProcess::ProcessError error)
+{
+ this->stop();
+ emit started(0, _ropass, _rwpass);
+}
+
+void VncServer::onFinished(int exitCode)
+{
+ this->stop();
+ emit started(0, _ropass, _rwpass);
+}
diff --git a/src/client/vnc/vncserver.h b/src/client/vnc/vncserver.h
new file mode 100644
index 0000000..2aae49c
--- /dev/null
+++ b/src/client/vnc/vncserver.h
@@ -0,0 +1,52 @@
+/*
+ * vncserver.h
+ *
+ * Created on: 24.01.2013
+ * Author: sr
+ */
+
+#ifndef VNCSERVER_H_
+#define VNCSERVER_H_
+
+#include <QtCore>
+
+class VncServer;
+
+class VncServer : public QObject
+{
+ Q_OBJECT
+
+private:
+ QProcess *_process;
+ QString _ropass;
+ QString _rwpass;
+ int _port;
+ int _timerId;
+
+ VncServer();
+ virtual ~VncServer();
+
+ static VncServer *me;
+
+public:
+ static VncServer *instance();
+
+ void start();
+ void stop();
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+signals:
+ // Emited when started succesfully, or if startup failed. port will be <= 0 if it failed.
+ void started(int port, QString& ropass, QString& rwpass);
+
+private slots:
+ void onStdOut();
+ void onStdErr();
+ void onFinished(int exitCode);
+ void onError(QProcess::ProcessError error);
+
+};
+
+#endif /* VNCSERVER_H_ */
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);
+}
diff --git a/src/client/vnc/vncthread.h b/src/client/vnc/vncthread.h
new file mode 100644
index 0000000..b6679b3
--- /dev/null
+++ b/src/client/vnc/vncthread.h
@@ -0,0 +1,141 @@
+/*
+ # 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/
+ */
+
+#ifndef VNCCLIENTTHREAD_H_
+#define VNCCLIENTTHREAD_H_
+
+#include <QtCore>
+#include <QImage>
+#include <QThread>
+
+class QPainter;
+
+extern "C"
+{
+#include <rfb/rfbclient.h>
+}
+
+/**
+ * - START -
+ * Event classes. Not my code. Might be useful to implement remote assistance via VNC later.
+ * Code might come from the KDE VNC client.
+ */
+class SomeEvent
+{
+public:
+ virtual ~SomeEvent(){}
+ virtual void fire(rfbClient*) = 0;
+};
+
+class KeyEvent : public SomeEvent
+{
+public:
+ KeyEvent(int key, int pressed) :
+ _key(key), _pressed(pressed)
+ {
+ }
+
+ void fire(rfbClient*);
+
+private:
+ int _key;
+ int _pressed;
+};
+
+class PointerEvent : public SomeEvent
+{
+public:
+ PointerEvent(int x, int y, int buttonMask) :
+ _x(x), _y(y), _buttonMask(buttonMask)
+ {
+ }
+
+ void fire(rfbClient*);
+
+private:
+ int _x;
+ int _y;
+ int _buttonMask;
+};
+/** - END - **/
+
+/**
+ * VncThread - communicate with VNC server, scale image if necessary.
+ *
+ * As this class is derived from QThread and overrides the run() method,
+ * it does not have an event-loop. Do NOT try to add any slots to it.
+ * It will NOT be able to receive signals. Emitting signals is fine though.
+ */
+class VncThread : public QThread
+{
+Q_OBJECT
+
+private:
+ rfbClient *_client;
+ quint8 *_frameBuffer;
+
+ QString _host;
+ int _port;
+ QString _passwd;
+ int _quality;
+ QQueue<SomeEvent*> _eventQueue;
+
+ QPainter *_painter;
+ QImage _img;
+ QImage _imgScaled;
+ QSize _clientSize;
+ QSize _localSize;
+ QMutex _mutex;
+
+ QSize _newLocalSize;
+ volatile bool _hasNewLocalSize;
+
+ int _srcStepX, _srcStepY, _dstStepX, _dstStepY;
+
+ bool _connected;
+ volatile bool _run;
+
+ void calcScaling();
+
+ // Callbacks for rfb lib. make them class members so the callbacks can access private members of the class.
+ static void updateImage(rfbClient *client, int x, int y, int w, int h);
+ static char* passwdHandler(rfbClient *client);
+ static rfbBool frameBufferHandler(rfbClient *client);
+
+public:
+ VncThread(QString host, int port, QString passwd, int quality);
+ // Do NOT delete this class directly. use VncThread::destroy() instead!
+ ~VncThread();
+
+ const QImage& getImage() const { if (_srcStepX > 1 || _srcStepY > 1) return _imgScaled; return _img; }
+ const QSize& getSourceSize() const { return _clientSize; }
+ const QString getDesktopName() const;
+ void processImageUpdate(const int x, const int y, const int w, const int h);
+ void mouseEvent(int x, int y, int buttonMask);
+ void keyEvent(int key, bool pressed);
+ const bool isConnected() const { return _connected; }
+ void stop() { _run = false; }
+ void setTargetSize(const QSize size);
+ void run();
+
+ int const static HIGH = 0;
+ int const static MEDIUM = 1;
+ int const static LOW = 2;
+
+signals:
+ void imageUpdated(const int x, const int y, const int w, const int h);
+ void projectionStarted();
+
+};
+
+#endif /* VNCCLIENTTHREAD_H_ */
diff --git a/src/client/vnc/vncwindow.cpp b/src/client/vnc/vncwindow.cpp
new file mode 100644
index 0000000..d4f6d40
--- /dev/null
+++ b/src/client/vnc/vncwindow.cpp
@@ -0,0 +1,277 @@
+/*
+ # 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/
+ # -----------------------------------------------------------------------------
+ # clientVNCViewer.cpp
+ # - connetct to vnc server and show remote screen (window/full)
+ # -----------------------------------------------------------------------------
+ */
+
+#include "vncwindow.h"
+#include "vncthread.h"
+
+VncWindow::VncWindow(QWidget *parent) :
+ QDialog(parent), _vncWorker(NULL), _viewOnly(true), _buttonMask(0), _clientId(0)
+{
+ //
+}
+
+VncWindow::~VncWindow()
+{
+ close();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Public
+
+void VncWindow::open(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId)
+{
+ // start thread for vnc-updates
+ this->onThreadFinished();
+ _clientId = clientId;
+ _vncWorker = new VncThread(host, port, passwd, 1);
+ connect(_vncWorker, SIGNAL(finished()), this, SLOT(onThreadFinished()), Qt::QueuedConnection);
+ connect(_vncWorker, SIGNAL(projectionStarted()), this, SLOT(onProjectionStarted()), Qt::QueuedConnection);
+ _vncWorker->start(QThread::LowPriority);
+ //_rfbclient = _thread->getRfbClient();
+ //installEventFilter(this);
+ setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
+ setFocusPolicy(Qt::WheelFocus); //needed?!?
+
+ setWindowTitle(caption);
+
+ setAttribute(Qt::WA_OpaquePaintEvent);
+
+ if (fullscreen)
+ {
+ setWindowFlags(Qt::WindowStaysOnTopHint);
+ showFullScreen();
+ activateWindow();
+ raise();
+ }
+ else
+ {
+ resize(800, 600);
+ showNormal();
+ }
+
+ this->show();
+ _vncWorker->setTargetSize(this->size());
+
+ connect(_vncWorker, SIGNAL(imageUpdated(const int, const int, const int, const int)), this,
+ SLOT(onUpdateImage(const int, const int, const int, const int)),
+ Qt::QueuedConnection);
+}
+
+void VncWindow::closeEvent(QCloseEvent *e)
+{
+ e->ignore();
+ qDebug("Closing VNC viewer window.");
+ this->setVisible(false);
+ this->onThreadFinished();
+ emit running(false, _clientId);
+}
+
+void VncWindow::onUpdateImage(const int x, const int y, const int w, const int h)
+{
+ this->repaint(x, y, w, h);
+}
+
+/**
+ * Thread finished, clean up and close window
+ */
+void VncWindow::onThreadFinished()
+{
+ if (_vncWorker)
+ {
+ disconnect(_vncWorker, SIGNAL(imageUpdated(const int, const int, const int, const int)), this,
+ SLOT(onUpdateImage(const int, const int, const int, const int)));
+ disconnect(_vncWorker, SIGNAL(finished()), this, SLOT(onThreadFinished()));
+ _vncWorker->stop();
+ delete _vncWorker;
+ _vncWorker = NULL;
+ this->close();
+ }
+}
+
+/**
+ * VNC Thread successfully connected to remote end - projection will start
+ */
+void VncWindow::onProjectionStarted()
+{
+ emit running(true, _clientId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected
+
+void VncWindow::paintEvent(QPaintEvent *event)
+{
+ const QRect &r = event->rect();
+ this->draw(r.left(), r.top(), r.width(), r.height());
+ event->accept();
+}
+
+void VncWindow::resizeEvent(QResizeEvent* event)
+{
+ _vncWorker->setTargetSize(event->size());
+}
+
+void VncWindow::draw(const int x, const int y, const int w, const int h)
+{
+ if (_vncWorker == NULL)
+ return;
+ QPainter painter(this);
+ painter.drawImage(x, y, _vncWorker->getImage(), x, y, w, h);
+ //painter.drawRect(x,y,w,h); // for debugging updated area
+}
+
+//returns true if event was processed
+/*bool VncWindow::event(QEvent *event)
+ {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+
+ keyEventHandler(static_cast<QKeyEvent*>(event));
+ return true;
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ mouseEventHandler(static_cast<QMouseEvent*>(event));
+ return true;
+ break;
+ case QEvent::Wheel:
+ wheelEventHandler(static_cast<QWheelEvent*>(event));
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }*/
+
+//handles mouseevents
+void VncWindow::mouseEventHandler(QMouseEvent *e)
+{
+ if (e->type() != QEvent::MouseMove)
+ {
+ if ((e->type() == QEvent::MouseButtonPress) || (e->type() == QEvent::MouseButtonDblClick))
+ {
+ if (e->button() & Qt::LeftButton)
+ _buttonMask |= 0x01;
+ if (e->button() & Qt::MidButton)
+ _buttonMask |= 0x02;
+ if (e->button() & Qt::RightButton)
+ _buttonMask |= 0x04;
+ }
+ else if (e->type() == QEvent::MouseButtonRelease)
+ {
+ if (e->button() & Qt::LeftButton)
+ _buttonMask &= 0xfe;
+ if (e->button() & Qt::MidButton)
+ _buttonMask &= 0xfd;
+ if (e->button() & Qt::RightButton)
+ _buttonMask &= 0xfb;
+ }
+ }
+ _vncWorker->mouseEvent(e->x(), e->y(), _buttonMask);
+}
+
+//handles mousewheel
+void VncWindow::wheelEventHandler(QWheelEvent *event)
+{
+ int eb = 0;
+ if (event->delta() < 0)
+ eb |= 0x10;
+ else
+ eb |= 0x8;
+
+ const int x = event->x();
+ const int y = event->y();
+
+ _vncWorker->mouseEvent(x, y, eb | _buttonMask);
+ _vncWorker->mouseEvent(x, y, _buttonMask);
+}
+
+//Handles keypress
+void VncWindow::keyEventHandler(QKeyEvent *e)
+{
+ rfbKeySym k = e->nativeVirtualKey();
+
+ // do not handle Key_Backtab separately because the Shift-modifier
+ // is already enabled
+ if (e->key() == Qt::Key_Backtab)
+ {
+ k = XK_Tab;
+ }
+
+ const bool pressed = (e->type() == QEvent::KeyPress);
+
+ // handle modifiers
+ if (k == XK_Shift_L || k == XK_Control_L || k == XK_Meta_L || k == XK_Alt_L)
+ {
+ if (pressed)
+ {
+ _modkeys[k] = true;
+ }
+ else if (_modkeys.contains(k))
+ {
+ _modkeys.remove(k);
+ }
+ else
+ {
+ unpressModifiers();
+ }
+ }
+
+ if (k)
+ {
+ _vncWorker->keyEvent(k, pressed);
+ }
+}
+
+void VncWindow::keyPressEvent(QKeyEvent* event)
+{
+ if (event->key() == Qt::Key_Escape)
+ this->close();
+}
+
+//removes modifier keys which have been pressed
+void VncWindow::unpressModifiers()
+{
+ const QList<unsigned int> keys = _modkeys.keys();
+ QList<unsigned int>::const_iterator it = keys.constBegin();
+ while (it != keys.end())
+ {
+ _vncWorker->keyEvent(*it, false);
+ it++;
+ }
+ _modkeys.clear();
+}
+
+//(QT Function) Filters events, if _viewOnly is set, true is returned and the event is ignored
+//TODO use this function when implementing viewonly switch
+bool VncWindow::eventFilter(QObject *obj, QEvent *event)
+{
+ if (_viewOnly)
+ {
+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease
+ || event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::MouseButtonPress
+ || event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::Wheel
+ || event->type() == QEvent::MouseMove)
+ return true;
+ }
+
+ return false;
+ //return RemoteView::eventFilter(obj, event);
+}
diff --git a/src/client/vnc/vncwindow.h b/src/client/vnc/vncwindow.h
new file mode 100644
index 0000000..3a73a8e
--- /dev/null
+++ b/src/client/vnc/vncwindow.h
@@ -0,0 +1,73 @@
+/*
+ # 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/
+ */
+
+#ifndef CLIENTVNCVIEWER_H_
+#define CLIENTVNCVIEWER_H_
+
+#include <QtGui>
+#include <QMouseEvent>
+
+class VncThread;
+class QPainter;
+
+// Definition of key modifier mask constants
+#define KMOD_Alt_R 0x01
+#define KMOD_Alt_L 0x02
+#define KMOD_Meta_L 0x04
+#define KMOD_Control_L 0x08
+#define KMOD_Shift_L 0x10
+
+class VncWindow : public QDialog
+{
+Q_OBJECT
+
+public:
+ VncWindow(QWidget *parent = 0);
+ virtual ~VncWindow();
+
+protected slots:
+ void onUpdateImage(const int x, const int y, const int w, const int h);
+ void onThreadFinished();
+ void onProjectionStarted();
+
+ void open(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId);
+
+signals:
+ void running(const bool isRunning, const int clientId);
+
+
+protected:
+ void draw(const int x, const int y, const int w, const int h);
+ void paintEvent(QPaintEvent *event);
+ void resizeEvent(QResizeEvent* event);
+ void closeEvent(QCloseEvent *e);
+ //bool event(QEvent *event);
+ //bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+ VncThread *_vncWorker;
+ bool _viewOnly;
+ int _buttonMask;
+ QMap<unsigned int, bool> _modkeys;
+ int _clientId;
+
+ bool eventFilter(QObject *obj, QEvent *event);
+ void keyPressEvent(QKeyEvent* event);
+ void keyEventHandler(QKeyEvent *e);
+ void unpressModifiers();
+ void wheelEventHandler(QWheelEvent *event);
+ void mouseEventHandler(QMouseEvent *event);
+
+};
+
+#endif /* CLIENTVNCVIEWER_H_ */