diff options
author | sr | 2013-02-04 19:50:31 +0100 |
---|---|---|
committer | sr | 2013-02-04 19:50:31 +0100 |
commit | 1a5709501f94014d41987b956338bb6424b9f90c (patch) | |
tree | d3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/client/vnc | |
parent | Test (diff) | |
download | pvs2-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.cpp | 191 | ||||
-rw-r--r-- | src/client/vnc/vncserver.h | 52 | ||||
-rw-r--r-- | src/client/vnc/vncthread.cpp | 314 | ||||
-rw-r--r-- | src/client/vnc/vncthread.h | 141 | ||||
-rw-r--r-- | src/client/vnc/vncwindow.cpp | 277 | ||||
-rw-r--r-- | src/client/vnc/vncwindow.h | 73 |
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_ */ |