summaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/client
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/client')
-rw-r--r--src/client/connectwindow/connectwindow.cpp284
-rw-r--r--src/client/connectwindow/connectwindow.h95
-rw-r--r--src/client/main.cpp41
-rw-r--r--src/client/net/serverconnection.cpp348
-rw-r--r--src/client/net/serverconnection.h62
-rw-r--r--src/client/toolbar/toolbar.cpp200
-rw-r--r--src/client/toolbar/toolbar.h78
-rwxr-xr-xsrc/client/util/platform/blankscreen.h25
-rwxr-xr-xsrc/client/util/platform/blankscreen_Win32.cpp41
-rwxr-xr-xsrc/client/util/platform/blankscreen_X11.cpp75
-rw-r--r--src/client/util/util.cpp25
-rw-r--r--src/client/util/util.h23
-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
18 files changed, 2345 insertions, 0 deletions
diff --git a/src/client/connectwindow/connectwindow.cpp b/src/client/connectwindow/connectwindow.cpp
new file mode 100644
index 0000000..bc4e9b2
--- /dev/null
+++ b/src/client/connectwindow/connectwindow.cpp
@@ -0,0 +1,284 @@
+/*
+ * connectwindow.cpp
+ *
+ * Created on: 28.01.2013
+ * Author: sr
+ */
+
+#include "connectwindow.h"
+#include "../../shared/settings.h"
+#include "../../shared/network.h"
+#include "../../shared/util.h"
+
+#include "../net/serverconnection.h"
+
+#include <QNetworkInterface>
+
+#define UDPBUFSIZ 9000
+#define SALT_LEN 18
+
+ConnectWindow::ConnectWindow(QWidget *parent) :
+ QDialog(parent), _connected(false), _timerDiscover(0), _timerHide(0), _connection(NULL), _state(Idle),
+ _hashErrorCount(0), _hashSslErrorCount(0), _certErrorCount(0), _ipErrorCount(0), _discoveryInterval(800)
+{
+ setupUi(this);
+ //
+ connect(cmdOK, SIGNAL(clicked()), this, SLOT(onOkClick()));
+ connect(cmdCancel, SIGNAL(clicked()), this, SLOT(onCancelClick()));
+ int tries = 10;
+ while (tries-- != 0)
+ {
+ const quint16 port = (quint16)(qrand() % 10000) + 10000;
+ if (_discoverySocket.bind(QHostAddress::Any, port))
+ break;
+ if (tries == 0)
+ qFatal("Could not bind to any UDP port for server discovery.");
+ }
+ connect(&_discoverySocket, SIGNAL(readyRead()), this, SLOT(onUdpReadyRead()));
+ this->setState(Idle);
+}
+
+ConnectWindow::~ConnectWindow()
+{
+
+}
+
+void ConnectWindow::setConnected(const bool connected)
+{
+ _connected = connected;
+ this->updateState();
+ if (_state == Scanning)
+ {
+ killTimer(_timerDiscover);
+ _discoveryInterval = 1000;
+ _timerDiscover = startTimer(_discoveryInterval);
+ }
+}
+
+void ConnectWindow::setState(const ConnectionState state)
+{
+ if (_state != state)
+ {
+ _state = state;
+ this->updateState();
+ }
+}
+
+void ConnectWindow::updateState()
+{
+ txtName->setEnabled(_state == Idle && !_connected);
+
+ if (_connected)
+ {
+ cmdOK->setEnabled(true);
+ cmdOK->setText(tr("&Disconnect"));
+ lblStatus->setText(tr("Connected."));
+ txtName->setEnabled(false);
+ return;
+ }
+
+ if (_state != Idle)
+ cmdOK->setText(tr("&Stop"));
+ else
+ cmdOK->setText(tr("&Connect"));
+
+ switch (_state)
+ {
+ case Idle:
+ lblStatus->setText(tr("Ready to connect; please enter session name."));
+ break;
+ case Scanning:
+ lblStatus->setText(tr("Scanning for session %1.").arg(txtName->text()));
+ _timerDiscover = startTimer(_discoveryInterval);
+ break;
+ case Connecting:
+ lblStatus->setText(tr("Found session, connecting..."));
+ break;
+ case AwaitingChallenge:
+ lblStatus->setText(tr("Waiting for server challenge..."));
+ break;
+ case AwaitingChallengeResponse:
+ lblStatus->setText(tr("Replied to challenge, sent own..."));
+ break;
+ case LoggingIn:
+ lblStatus->setText(tr("Logging in..."));
+ break;
+ case Connected:
+ lblStatus->setText(tr("Connection established!"));
+ break;
+ case InvalidIpList:
+ case InvalidHash:
+ case InvalidCert:
+ case InvalidSslHash:
+ lblError->setText(tr("Invalid hash: %1; invalid cert: %2; invalid iplist: %3; invalid sslhash: %4")
+ .arg(_hashErrorCount).arg(_certErrorCount).arg(_ipErrorCount).arg(_hashSslErrorCount));
+ break;
+ }
+}
+
+/**
+ * Overrides
+ */
+
+void ConnectWindow::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _timerDiscover)
+ {
+ killTimer(_timerDiscover);
+ if (_connected || _state != Scanning) // Not scanning, bail out
+ return;
+ if (_discoveryInterval < 30000)
+ _discoveryInterval += 100;
+ _timerDiscover = startTimer(_discoveryInterval);
+ // Don't send packet if we're trying to connect
+ if (_connection != NULL)
+ return;
+ // Send discovery
+ _packet.reset();
+ QByteArray iplist(Network::interfaceAddressesToString().toUtf8());
+ QByteArray salt1(SALT_LEN, 0);
+ if (_salt2.size() < SALT_LEN)
+ _salt2.resize(SALT_LEN);
+ for (int i = 0; i < SALT_LEN; ++i)
+ {
+ salt1[i] = qrand() & 0xff;
+ _salt2[i] = qrand() & 0xff;
+ }
+ _packet.reset();
+ _packet.setField(_HASH, genSha1(&_nameBytes, &salt1, &iplist));
+ _packet.setField(_SALT1, salt1);
+ _packet.setField(_SALT2, _salt2);
+ _packet.setField(_IPLIST, iplist);
+ foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces())
+ {
+ foreach (QNetworkAddressEntry entry, interface.addressEntries())
+ {
+ if (!entry.broadcast().isNull() && entry.ip() != QHostAddress::LocalHost && entry.ip() != QHostAddress::LocalHostIPv6)
+ {
+ qDebug() << "Broadcasting to " << entry.broadcast().toString();
+ if (!_packet.writeMessage(&_discoverySocket, entry.broadcast(), SERVICE_DISCOVERY_PORT))
+ qDebug("FAILED");
+ }
+ }
+ }
+ qDebug("Broadcasting to 255.255.255.255");
+ if (!_packet.writeMessage(&_discoverySocket, QHostAddress::Broadcast, SERVICE_DISCOVERY_PORT))
+ qDebug("FAILED");
+ // End send discovery
+ }
+ else if(event->timerId() == _timerHide)
+ {
+ killTimer(_timerHide);
+ _timerHide = 0;
+ this->hide();
+ }
+ else
+ // Unknown/Old timer id, kill it
+ killTimer(event->timerId());
+}
+
+void ConnectWindow::closeEvent(QCloseEvent *e)
+{
+ e->ignore();
+ this->hide();
+}
+
+/**
+ * Slots
+ */
+
+void ConnectWindow::onOkClick()
+{
+ if (_timerHide)
+ killTimer(_timerHide);
+ _timerHide = 0;
+ if (_timerDiscover)
+ killTimer(_timerDiscover);
+ if (_connected || _state != Idle)
+ {
+ // Stop or disconnect
+ _timerDiscover = 0;
+ emit disconnect();
+ this->setState(Idle);
+ }
+ else
+ {
+ // Connect (scan for session)
+ _discoveryInterval = 800;
+ _nameBytes = txtName->text().toUtf8();
+ _timerDiscover = startTimer(_discoveryInterval);
+ _hashErrorCount = _hashSslErrorCount = _certErrorCount = _ipErrorCount = 0;
+ this->setState(Scanning);
+ }
+}
+
+void ConnectWindow::onCancelClick()
+{
+ this->hide();
+}
+
+void ConnectWindow::onUdpReadyRead()
+{
+ char data[UDPBUFSIZ];
+ QHostAddress addr;
+ quint16 port;
+ while (_discoverySocket.hasPendingDatagrams())
+ {
+ const qint64 size = _discoverySocket.readDatagram(data, UDPBUFSIZ, &addr, &port);
+ if (size <= 0 || _connection != NULL)
+ continue;
+
+ _packet.reset();
+ if (!_packet.readMessage(data, (quint32)size))
+ continue;
+ // Valid packet, process it:
+ const QByteArray hash(_packet.getFieldBytes(_HASH));
+ const QByteArray iplist(_packet.getFieldBytes(_IPLIST));
+ const QByteArray port(_packet.getFieldBytes(_PORT));
+ const QByteArray cert(_packet.getFieldBytes(_CERT));
+ // Check if the source IP of the packet matches any of the addresses given in the IP list
+ if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString()))
+ {
+ ++_ipErrorCount;
+ this->setState(InvalidIpList);
+ this->setState(Scanning);
+ continue;
+ }
+ // If so, check if the submitted hash seems valid
+ if (genSha1(&_nameBytes, &_salt2, &iplist, &port, &cert) != hash)
+ {
+ // did not match local session name, or other data was spoofed
+ ++_hashErrorCount;
+ this->setState(InvalidHash);
+ this->setState(Scanning);
+ continue;
+ }
+ // Otherwise it's a valid reply, try to connect
+ _connection = new ServerConnection(addr.toString(), (quint16)QString::fromUtf8(port).toInt(), _nameBytes, cert);
+ connect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState)));
+ connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*)));
+ }
+}
+
+void ConnectWindow::onConnectionStateChange(ConnectWindow::ConnectionState state)
+{
+ bool reset = (_state == Scanning);
+ if (state == InvalidSslHash)
+ ++_hashSslErrorCount;
+ this->setState(state);
+ if (reset)
+ _state = Scanning;
+ if (state == Connected)
+ {
+ QObject::disconnect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState)));
+ QObject::disconnect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*)));
+ emit connected(_connection);
+ _connection = NULL;
+ _timerHide = startTimer(2000);
+ }
+}
+
+void ConnectWindow::onConnectionClosed(QObject* connection)
+{
+ _connection = NULL;
+}
diff --git a/src/client/connectwindow/connectwindow.h b/src/client/connectwindow/connectwindow.h
new file mode 100644
index 0000000..d8f9ab9
--- /dev/null
+++ b/src/client/connectwindow/connectwindow.h
@@ -0,0 +1,95 @@
+/*
+ # Copyright (c) 2013 - 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/
+ # ---------------------------------------------------------------------
+ # - Allow user to connect/disconnect to/from server
+ */
+
+#ifndef PVSCONNECTWINDOW_H_
+#define PVSCONNECTWINDOW_H_
+
+#include <QtGui>
+#include <QUdpSocket>
+#include <QSslSocket>
+#include "ui_connect.h"
+
+#include "../../shared/networkmessage.h"
+
+class ServerConnection;
+
+class ConnectWindow : public QDialog, private Ui_Dialog
+{
+Q_OBJECT
+
+public:
+ enum ConnectionState
+ {
+ Idle,
+ Scanning,
+ Connecting,
+ AwaitingChallenge,
+ AwaitingChallengeResponse,
+ LoggingIn,
+ InvalidIpList,
+ InvalidHash, // Hash of UDP reply invalid
+ InvalidSslHash, // Hash of challenge inside SSL connection invalid
+ InvalidCert,
+ Connected
+ };
+
+private:
+ bool _connected;
+ int _timerDiscover, _timerHide;
+ ServerConnection *_connection;
+ ConnectionState _state;
+ int _hashErrorCount, _hashSslErrorCount, _certErrorCount, _ipErrorCount;
+ int _discoveryInterval;
+
+ QByteArray _nameBytes;
+ QByteArray _salt2;
+ QUdpSocket _discoverySocket;
+ NetworkMessage _packet;
+
+ void setState(const ConnectionState state);
+ void updateState();
+
+public:
+ ConnectWindow(QWidget *parent = NULL);
+ virtual ~ConnectWindow();
+
+ void setConnected(const bool connected);
+
+protected:
+ /*
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ */
+ void timerEvent(QTimerEvent* event);
+ void closeEvent(QCloseEvent *e);
+
+protected slots:
+ void onOkClick();
+ void onCancelClick();
+ void onUdpReadyRead();
+ void onConnectionStateChange(ConnectWindow::ConnectionState state);
+ void onConnectionClosed(QObject* connection);
+ //void onSsl
+
+signals:
+ void disconnect();
+ void connected(ServerConnection* connection);
+
+};
+
+#endif
diff --git a/src/client/main.cpp b/src/client/main.cpp
new file mode 100644
index 0000000..cea9ae8
--- /dev/null
+++ b/src/client/main.cpp
@@ -0,0 +1,41 @@
+#include "toolbar/toolbar.h"
+#include "util/util.h"
+
+int main(int argc, char** argv)
+{
+ QApplication app(argc, argv);
+ app.setOrganizationName("openslx");
+ app.setOrganizationDomain("openslx.org");
+ app.setApplicationName("pvsclient");
+
+ qsrand((uint)QDateTime::currentMSecsSinceEpoch());
+
+ // Make sure settings directory exists
+ do {
+ USER_SETTINGS(settings);
+ QFileInfo fi(settings.fileName());
+ QDir path(fi.path());
+ qDebug() << "Settings directory is " << fi.path();
+ if (!path.exists())
+ path.mkpath(path.absolutePath());
+ // Now check if settings file exists. If not, copy system default (if available)
+ if (!fi.exists())
+ {
+ SYSTEM_SETTINGS(sys);
+ QFileInfo sysfi(sys.fileName());
+ if (sysfi.exists())
+ {
+ if (!QFile::copy(sys.fileName(), settings.fileName()))
+ qDebug() << "Copying default settings from " << sys.fileName() << " to " << settings.fileName() << " failed.";
+ }
+ }
+ } while (0);
+
+ // use system locale as language to translate gui
+ QTranslator translator;
+ translator.load(":pvsclient");
+ app.installTranslator(&translator);
+
+ Toolbar pvsclient;
+ return app.exec();
+}
diff --git a/src/client/net/serverconnection.cpp b/src/client/net/serverconnection.cpp
new file mode 100644
index 0000000..849f43b
--- /dev/null
+++ b/src/client/net/serverconnection.cpp
@@ -0,0 +1,348 @@
+#include "serverconnection.h"
+#include <QtCore>
+#include <QPixmap>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QHostInfo>
+#include <unistd.h>
+#include <cstdlib>
+//#define verbose
+#include "../vnc/vncserver.h"
+
+#include "../../shared/util.h"
+#include "../util/platform/blankscreen.h"
+
+#define CHALLENGE_LEN 20
+
+ServerConnection::ServerConnection(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash) :
+ QObject(NULL), _jpegQuality(80), _authed(0), _sessionName(sessionName), _certHash(certHash)
+{
+ _socket = new QSslSocket();
+ _blank = new BlankScreen();
+ connect(_socket, SIGNAL(encrypted()), this, SLOT(sock_connected()));
+ connect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival()));
+ connect(_socket, SIGNAL(disconnected()), this, SLOT(sock_closed()));
+ connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError)));
+ connect(_socket,
+ SIGNAL(sslErrors(const QList<QSslError> &)),
+ this,
+ SLOT(sslErrors(const QList<QSslError> &))
+ );
+ qDebug("Connecting to %s on port %d", host.toUtf8().data(), (int)port);
+ _socket->connectToHostEncrypted(host, port);
+ _timerId = startTimer(4000);
+ // Connect the vnc start/stop signal to this class, so we can tell the server about successful vnc server startup
+ connect(VncServer::instance(), SIGNAL(started(int, QString&, QString&)), this, SLOT(onVncServerStartStop(int, QString&, QString&)));
+}
+
+ServerConnection::~ServerConnection()
+{
+ this->disconnectFromServer();
+ _blank->deleteLater();
+ _blank = NULL;
+}
+
+void ServerConnection::sendMessage(NetworkMessage& message)
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ return;
+ message.writeMessage(_socket);
+ if (!message.writeComplete())
+ {
+ qCritical() << "SendMessage to server failed!";
+ }
+}
+
+
+void ServerConnection::disconnectFromServer()
+{
+ if (_socket == NULL)
+ return;
+ qDebug("Closing connection to server");
+ VncServer::instance()->stop();
+ emit closeVnc();
+ _socket->blockSignals(true);
+ _socket->abort();
+ _socket->deleteLater();
+ _socket = NULL;
+ this->deleteLater();
+}
+
+void ServerConnection::handleMsg()
+{
+ const QString &id = _fromServer.getFieldString(_ID);
+
+ if (_authed == 0)
+ {
+ if (id == _CHALLENGE)
+ {
+ // Initial challenge request by server
+ emit stateChange(ConnectWindow::AwaitingChallengeResponse);
+ _myChallenge.resize(CHALLENGE_LEN);
+ for (int i = 0; i < CHALLENGE_LEN; ++i)
+ _myChallenge[i] = qrand() & 0xff;
+ QByteArray serverChallenge(_fromServer.getFieldBytes(_CHALLENGE));
+ _toServer.reset();
+ _toServer.setField(_ID, _CHALLENGE);
+ _toServer.setField(_HASH, genSha1(&_sessionName, &serverChallenge));
+ _toServer.setField(_CHALLENGE, _myChallenge);
+ _toServer.writeMessage(_socket);
+ qDebug("Received challenge, replying, sending own challenge step <- 1");
+ _authed = 1;
+ }
+ return;
+ }
+
+ if (_authed == 1)
+ {
+ if (id == _CHALLENGE)
+ {
+ qDebug("Received challenge reply");
+ if (_timerId != 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ // Check challenge response
+ QByteArray serverhash(_fromServer.getFieldBytes(_HASH));
+ if (serverhash != genSha1(&_sessionName, &_myChallenge))
+ {
+ qDebug("invalid. STOP.");
+ emit stateChange(ConnectWindow::InvalidSslHash);
+ this->disconnectFromServer();
+ return;
+ }
+ emit stateChange(ConnectWindow::LoggingIn);
+ char *user = getlogin();
+ if (user == NULL || *user == '\0')
+ user = getenv("USER");
+ if (user == NULL || *user == '\0')
+ user = getenv("USERNAME");
+ if (user == NULL || *user == '\0')
+ user = (char*)"Hans Affe";
+ _toServer.reset();
+ _toServer.setField(_ID, _LOGIN);
+ _toServer.setField("HOST", QHostInfo::localHostName());
+ _toServer.setField("NAME", QString(user));
+ qDebug("Sending login request!");
+ if (_toServer.writeMessage(_socket))
+ {
+ _authed = 2;
+ qDebug("valid, step <- 2");
+ }
+ else
+ {
+ this->disconnectFromServer();
+ }
+ }
+ return;
+ }
+
+ if (_authed == 2)
+ {
+ if (id == _LOGIN)
+ {
+ qDebug("login accepted, step <- 3");
+ _authed = 3;
+ emit stateChange(ConnectWindow::Connected);
+ }
+ return;
+ }
+
+ // message THUMB - server requests screenshot as thumbnail
+ if (id == _THUMB)
+ {
+ int x = _fromServer.getFieldString(_X).toInt();
+ int y = _fromServer.getFieldString(_Y).toInt();
+ if (x < 32)
+ x = 32;
+ else if (x > 400)
+ x = 400;
+ if (y < 18)
+ y = 18;
+ else if (y > 300)
+ y = 300;
+ QPixmap desktop(QPixmap::grabWindow(QApplication::desktop()->winId()).scaled(
+ x, y,
+ Qt::KeepAspectRatio,
+ Qt::SmoothTransformation));
+ QByteArray bytes;
+ QBuffer jpgBuffer(&bytes);
+ jpgBuffer.open(QIODevice::WriteOnly);
+ qDebug("JPEG quality is %d", _jpegQuality);
+ if (desktop.save(&jpgBuffer, "JPG", _jpegQuality)) // writes pixmap into bytes in JPG format
+ {
+ // Try to adjust quality so we stay between 3 and 4.5 KB
+ if (_jpegQuality < 90 && bytes.size() < 3000)
+ _jpegQuality += 7;
+ else if (_jpegQuality > 40 && bytes.size() > 4500)
+ _jpegQuality -= 7;
+ }
+ else
+ { // FALLBACK
+ bytes.clear();
+ QBuffer pngBuffer(&bytes);
+ pngBuffer.open(QIODevice::WriteOnly);
+ if (!desktop.save(&pngBuffer, "PNG")) // writes pixmap into bytes in PNG format
+ {
+ qDebug("Could not convert screenshot to PNG nor JPG");
+ return; // Failed :-(
+ }
+ }
+ _toServer.reset();
+ _toServer.setField(_ID, _THUMB);
+ _toServer.setField(_IMG, bytes);
+ sendMessage(_toServer);
+ } // message VNCSERVER - start local vncserver
+ else if (id == _VNCSERVER)
+ {
+ const bool enable = (_fromServer.getFieldString("ENABLE").toInt() != 0);
+ if (enable)
+ {
+ emit closeVnc(); // In case we are watching some other client, stop doing so
+ VncServer::instance()->start();
+ }
+ else
+ {
+ VncServer::instance()->stop();
+ }
+ }
+ else if (id == _VNCCLIENT)
+ {
+ const QString host(_fromServer.getFieldString("HOST"));
+ const int port = _fromServer.getFieldString("PORT").toInt();
+ if (host.isEmpty() || port <= 0)
+ {
+ emit closeVnc();
+ }
+ else
+ {
+ emit openVnc(host, port, _fromServer.getFieldString("ROPASS"), true, true, _fromServer.getFieldString("CAPTION"), _fromServer.getFieldString("CLIENTID").toInt());
+ }
+ }
+ else if (id == _LOCK)
+ {
+ const bool enable = (_fromServer.getFieldString("ENABLE").toInt() != 0);
+ if (enable)
+ _blank->lock(_fromServer.getFieldString("MESSAGE"));
+ else
+ _blank->unlock();
+ }
+}
+
+/**
+ * Override
+ */
+
+void ServerConnection::timerEvent(QTimerEvent *event)
+{
+ if (_timerId == 0)
+ return;
+ killTimer(_timerId);
+ _timerId = 0;
+ this->disconnectFromServer();
+}
+
+/**
+ * Slots
+ */
+
+void ServerConnection::onVncServerStartStop(int port, QString& ropass, QString& rwpass)
+{
+ _toServer.reset();
+ _toServer.setField(_ID, _VNCSERVER);
+ if (port <= 0)
+ {
+ _toServer.setField("PORT", QByteArray("0"));
+ }
+ else
+ {
+ _toServer.setField("PORT", QString::number(port));
+ _toServer.setField("ROPASS", ropass);
+ _toServer.setField("RWPASS", rwpass);
+ }
+ sendMessage(_toServer);
+}
+
+void ServerConnection::onVncViewerStartStop(const bool started, const int clientId)
+{
+ _toServer.reset();
+ _toServer.setField(_ID, _VNCCLIENT);
+ if (started)
+ _toServer.setField("ENABLED", QByteArray("1"));
+ else
+ _toServer.setField("ENABLED", QByteArray("0"));
+ _toServer.setField("CLIENTID", QString::number(clientId));
+ sendMessage(_toServer);
+}
+
+void ServerConnection::sslErrors(const QList<QSslError> & errors)
+{
+ for (QList<QSslError>::const_iterator it = errors.begin(); it != errors.end(); it++)
+ {
+ QSslError err = *it;
+ qDebug("Connect SSL: %s", qPrintable(err.errorString()));
+ if (err.error() == QSslError::HostNameMismatch)
+ continue; // We don't pay attention to hostnames for validation
+ if (err.error() == QSslError::SelfSignedCertificate)
+ continue; // Also, this will always be the case; we check the fingerprint later
+ if (err.error() == QSslError::CertificateNotYetValid || err.error() == QSslError::CertificateExpired)
+ continue;
+ // Some other SSL error - do not ignore
+ return;
+ }
+ _socket->ignoreSslErrors();
+}
+
+void ServerConnection::sock_dataArrival()
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ {
+ qDebug("dataArrival called in bad state");
+ return;
+ }
+
+ while (_socket->bytesAvailable())
+ {
+ bool retval;
+ retval = _fromServer.readMessage(_socket); // let the message read data from socket
+ if (!retval) // error parsing msg, disconnect client!
+ {
+ this->disconnectFromServer();
+ return;
+ }
+ if (_fromServer.readComplete()) // message is complete
+ {
+ this->handleMsg();
+ if (_socket == NULL)
+ return;
+ _fromServer.reset();
+ }
+ }
+}
+
+void ServerConnection::sock_closed()
+{
+ // should this be unreliable in some way i suggest using the signal "stateChanged()" instead
+ // and check if the state changed to unconnected.
+ qDebug("Socket was closed... oh well..");
+ this->disconnectFromServer();
+}
+
+void ServerConnection::sock_error(QAbstractSocket::SocketError errcode)
+{
+ qDebug("Connection error: %d", (int)errcode);
+ this->disconnectFromServer();
+}
+
+void ServerConnection::sock_connected()
+{
+ QByteArray cert(_socket->peerCertificate().digest(QCryptographicHash::Sha1));
+ if (_certHash != cert)
+ {
+ emit stateChange(ConnectWindow::InvalidCert);
+ this->disconnectFromServer();
+ return;
+ }
+ emit stateChange(ConnectWindow::AwaitingChallenge);
+}
diff --git a/src/client/net/serverconnection.h b/src/client/net/serverconnection.h
new file mode 100644
index 0000000..afb8204
--- /dev/null
+++ b/src/client/net/serverconnection.h
@@ -0,0 +1,62 @@
+#ifndef _PVSSERVERCONNECTION_H_
+#define _PVSSERVERCONNECTION_H_
+
+#include <QSslSocket>
+#include "../../shared/networkmessage.h"
+#include "../connectwindow/connectwindow.h"
+
+class BlankScreen;
+
+class ServerConnection : public QObject
+{
+Q_OBJECT
+
+private:
+ QSslSocket *_socket;
+ BlankScreen *_blank;
+ int _timerId;
+ int _jpegQuality;
+ int _authed;
+
+ NetworkMessage _fromServer, _toServer;
+ QByteArray _expectedFingerprint;
+ QString _passwd;
+ QByteArray _myChallenge;
+ QByteArray _sessionName;
+ QByteArray _certHash;
+
+ void handleMsg();
+
+public:
+ ServerConnection(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash);
+ void disconnectFromServer();
+ ~ServerConnection();
+ const inline bool isConnected() const
+ {
+ return _socket != NULL && _socket->state() == QAbstractSocket::ConnectedState;
+ }
+
+ void sendMessage(NetworkMessage& message);
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private slots:
+ void sslErrors(const QList<QSslError> & errors); // triggered for errors that occur during SSL negotiation
+ void sock_dataArrival(); // triggered if data is available for reading
+ void sock_closed(); // triggered if the socket is closed
+ void sock_error(QAbstractSocket::SocketError errcode); // triggered if an error occurs on the socket
+ void sock_connected(); // triggered if the connection is established and ready to use
+
+ void onVncServerStartStop(int port, QString& ropass, QString& rwpass); // triggered if the local vnc server was started
+
+ void onVncViewerStartStop(const bool started, const int clientId);
+
+signals:
+ void openVnc(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId);
+ void closeVnc();
+ void stateChange(ConnectWindow::ConnectionState state);
+
+};
+
+#endif
diff --git a/src/client/toolbar/toolbar.cpp b/src/client/toolbar/toolbar.cpp
new file mode 100644
index 0000000..416a68f
--- /dev/null
+++ b/src/client/toolbar/toolbar.cpp
@@ -0,0 +1,200 @@
+/*
+ * toolbar.cpp
+ *
+ * Created on: 21.01.2013
+ * Author: sr
+ */
+
+#include "toolbar.h"
+#include "../../shared/settings.h"
+#include "../net/serverconnection.h"
+#include "../vnc/vncwindow.h"
+#include "../vnc/vncserver.h"
+
+Toolbar::Toolbar(QWidget *parent) :
+ QWidget(parent), _location(POSITION_TOP_CENTER), _hideTimer(0), _connection(NULL)
+{
+ setupUi(this);
+ setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
+ setAttribute(Qt::WA_AlwaysShowToolTips);
+ setAttribute(Qt::WA_QuitOnClose);
+ setVisible(true);
+ // VNC Window
+ _vnc = new VncWindow(NULL);
+ // Connect window
+ _connectWindow = new ConnectWindow(NULL);
+ connect(_connectWindow, SIGNAL(disconnect()), this, SLOT(onDoDisconnect()));
+ connect(_connectWindow, SIGNAL(connected(ServerConnection*)), this, SLOT(onConnected(ServerConnection*)));
+ //
+ setupMenu();
+ setLocation();
+ hideBar();
+}
+
+void Toolbar::setupMenu()
+{
+ _menu = new QMenu(this);
+ // setup actions
+ _acnDisconnect = new QAction(tr("&Connect"), this);
+ //_acnDisconnect->setEnabled(false);
+ _acnQuit = new QAction(tr("&Quit"), this);
+
+ // setup menu
+ _menu->addAction(_acnDisconnect);
+ _menu->addSeparator();
+ _menu->addAction(_acnQuit);
+
+ cmdMenu->setMenu(_menu);
+
+ connect(_acnQuit, SIGNAL(triggered()), this, SLOT(onQuit()));
+ connect(_acnDisconnect, SIGNAL(triggered()), _connectWindow, SLOT(show()));
+}
+
+Toolbar::~Toolbar()
+{
+ VncServer::instance()->stop();
+ _vnc->deleteLater();
+ _connectWindow->deleteLater();
+}
+
+//###########\\/\/
+
+void Toolbar::setLocation()
+{
+ const QDesktopWidget desktop;
+ const QRect primaryScreen = desktop.screenGeometry();
+ switch (_location)
+ {
+ case POSITION_TOP_LEFT:
+ move(primaryScreen.left(), primaryScreen.top());
+ break;
+ case POSITION_TOP_CENTER:
+ move((primaryScreen.width() - this->width()) / 2 + primaryScreen.left(), primaryScreen.top());
+ break;
+ case POSITION_TOP_RIGHT:
+ move(primaryScreen.right() - width(), primaryScreen.top());
+ break;
+ case POSITION_BOTTOM_LEFT:
+ move(primaryScreen.left(), primaryScreen.bottom() - height());
+ break;
+ case POSITION_BOTTOM_CENTER:
+ move((primaryScreen.width() - this->width()) / 2 + primaryScreen.left(), primaryScreen.bottom() - height());
+ break;
+ case POSITION_BOTTOM_RIGHT:
+ move(primaryScreen.right() - width(), primaryScreen.bottom() - height());
+ break;
+ default:
+ break;
+ }
+}
+
+void Toolbar::setBarVisible(bool shown)
+{
+ const QDesktopWidget desktop;
+ const QRect primaryScreen = desktop.screenGeometry();
+ if (!shown)
+ {
+ if (_location <= POSITION_TOP_RIGHT)
+ move(x(), primaryScreen.top() + 2 - height());
+ else
+ move(x(), primaryScreen.bottom() - 2);
+ }
+ else
+ {
+ if (_location <= POSITION_TOP_RIGHT)
+ move(x(), primaryScreen.top());
+ else
+ move(x(), primaryScreen.bottom() - height());
+ }
+}
+
+bool Toolbar::hideBar()
+{
+ if (_menu->isVisible()) // Don't hide window if any menu is open
+ return false;
+ setBarVisible(false);
+ return true;
+}
+
+/**
+ * Override
+ */
+
+void Toolbar::leaveEvent(QEvent* e)
+{
+ if (_hideTimer == 0)
+ _hideTimer = startTimer(100);
+ _hideDelay = 6;
+ QWidget::leaveEvent(e);
+}
+
+void Toolbar::enterEvent(QEvent* e)
+{
+ if (_hideTimer != 0)
+ {
+ killTimer(_hideTimer);
+ _hideTimer = 0;
+ }
+ setBarVisible(true);
+ QWidget::enterEvent(e);
+}
+
+void Toolbar::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _hideTimer)
+ {
+ if (--_hideDelay <= 0)
+ {
+ if (hideBar())
+ {
+ killTimer(_hideTimer);
+ _hideTimer = 0;
+ }
+ }
+ }
+}
+
+/**
+ * Slots
+ */
+
+void Toolbar::onDisconnected(QObject* connection)
+{
+ if (connection != _connection)
+ qDebug("onDisconnect pointer mismatch!");
+ _connectWindow->setConnected(false);
+ _connection = NULL;
+ lblStatus->setStyleSheet("color:red");
+ lblStatus->setText(tr("Offline"));
+}
+
+void Toolbar::onConnected(ServerConnection* connection)
+{
+ lblStatus->setStyleSheet("color:green");
+ lblStatus->setText(tr("Online"));
+ //
+ if (_connection != NULL)
+ {
+ disconnect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onDisconnected(QObject*)));
+ _connection->blockSignals(true);
+ _connection->disconnectFromServer();
+ }
+ _connection = connection;
+ connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onDisconnected(QObject*)));
+ connect(_connection, SIGNAL(openVnc(const QString&, int, const QString&, bool, bool, const QString&, const int)),
+ _vnc, SLOT(open(const QString&, int, const QString&, bool, bool, const QString&, const int)));
+ connect(_connection, SIGNAL(closeVnc()), _vnc, SLOT(close()));
+ connect(_vnc, SIGNAL(running(const bool, const int)), _connection, SLOT(onVncViewerStartStop(const bool, const int)));
+ _connectWindow->setConnected(true);
+}
+
+void Toolbar::onDoDisconnect()
+{
+ if (_connection != NULL)
+ _connection->disconnectFromServer();
+}
+
+void Toolbar::onQuit()
+{
+ QApplication::exit(0);
+}
diff --git a/src/client/toolbar/toolbar.h b/src/client/toolbar/toolbar.h
new file mode 100644
index 0000000..0ce4d2b
--- /dev/null
+++ b/src/client/toolbar/toolbar.h
@@ -0,0 +1,78 @@
+/*
+ # 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 PVSCLIENTGUI_H_
+#define PVSCLIENTGUI_H_
+
+#include <QtGui>
+#include "ui_toolbar.h"
+
+class ServerConnection;
+class VncWindow;
+class ConnectWindow;
+class BlankScreen;
+
+class Toolbar : public QWidget, private Ui_ToolbarClass
+{
+Q_OBJECT
+
+private:
+ int _location;
+ int _hideTimer;
+ int _hideDelay;
+ QMenu *_menu;
+ QAction *_acnDisconnect;
+ QAction *_acnQuit;
+ ServerConnection *_connection;
+
+ ConnectWindow *_connectWindow;
+ VncWindow *_vnc;
+
+ void setLocation();
+ bool hideBar();
+ void setBarVisible(bool shown);
+ void setupMenu();
+
+ void leaveEvent(QEvent* e);
+ void enterEvent(QEvent* e);
+ void timerEvent(QTimerEvent* event);
+
+public:
+ Toolbar(QWidget *parent = NULL);
+ virtual ~Toolbar();
+
+ int const static POSITION_TOP_LEFT = 0;
+ int const static POSITION_TOP_CENTER = 1;
+ int const static POSITION_TOP_RIGHT = 2;
+ int const static POSITION_BOTTOM_LEFT = 3;
+ int const static POSITION_BOTTOM_CENTER = 4;
+ int const static POSITION_BOTTOM_RIGHT = 5;
+
+protected:
+ /*
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ */
+
+private slots:
+ void onDisconnected(QObject* connection);
+ void onConnected(ServerConnection* connection);
+ void onDoDisconnect();
+ void onQuit();
+
+};
+
+#endif /* PVSCLIENTGUI_H_ */
diff --git a/src/client/util/platform/blankscreen.h b/src/client/util/platform/blankscreen.h
new file mode 100755
index 0000000..8ed4c04
--- /dev/null
+++ b/src/client/util/platform/blankscreen.h
@@ -0,0 +1,25 @@
+#ifndef _BLANKSCREEN_H_
+#define _BLANKSCREEN_H_
+
+#include <QString>
+#include <QDialog>
+
+class BlankScreen_Sysdep;
+
+class BlankScreen : public QDialog
+{
+ Q_OBJECT
+public:
+ BlankScreen();
+ virtual ~BlankScreen();
+ void draw(bool force = false);
+ bool lock(const QString& message);
+ bool unlock();
+
+private:
+ bool _locked;
+ QString _message;
+ BlankScreen_Sysdep* _sysdep;
+};
+
+#endif
diff --git a/src/client/util/platform/blankscreen_Win32.cpp b/src/client/util/platform/blankscreen_Win32.cpp
new file mode 100755
index 0000000..2bba1cf
--- /dev/null
+++ b/src/client/util/platform/blankscreen_Win32.cpp
@@ -0,0 +1,41 @@
+
+#include "blankscreen.h"
+#include <qwidget.h>
+
+struct BlankScreen_Sysdep {
+
+ bool locked;
+ QWidget* blankwin;
+
+ QString lockMsg;
+ int blackColor, whiteColor;
+ int offX, offY;
+};
+
+BlankScreen::BlankScreen()
+{
+ _sysdep = new BlankScreen_Sysdep;
+ _sysdep->blankwin = new QWidget(0, Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
+ _sysdep->blankwin->setWindowState(Qt::WindowFullScreen);
+ _sysdep->blankwin->show();
+}
+
+BlankScreen::~BlankScreen()
+{
+ delete _sysdep;
+}
+
+void BlankScreen::draw(bool force)
+{
+
+}
+
+bool BlankScreen::lock(const QString& message)
+{
+ return true;
+}
+
+bool BlankScreen::unlock()
+{
+ return true;
+}
diff --git a/src/client/util/platform/blankscreen_X11.cpp b/src/client/util/platform/blankscreen_X11.cpp
new file mode 100755
index 0000000..69b6d8c
--- /dev/null
+++ b/src/client/util/platform/blankscreen_X11.cpp
@@ -0,0 +1,75 @@
+#include "blankscreen.h"
+
+#include <QApplication>
+#include <QDesktopWidget>
+
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+
+#include <cassert>
+#include <cstring>
+
+struct BlankScreen_Sysdep
+{
+ Display *dpy;
+};
+
+BlankScreen::BlankScreen() : QDialog(NULL)
+{
+ _sysdep = new BlankScreen_Sysdep;
+ _sysdep->dpy = XOpenDisplay(NULL);
+ if (_sysdep->dpy == NULL)
+ return;
+ assert(_sysdep->dpy);
+
+ setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
+ setStyleSheet("background-color:#000");
+
+ _locked = false;
+}
+
+BlankScreen::~BlankScreen()
+{
+ unlock();
+ delete _sysdep;
+}
+
+bool BlankScreen::lock(const QString& message)
+{
+ if (_locked)
+ return true;
+ if (_sysdep->dpy == NULL)
+ return false;
+
+ _message = message;
+
+ this->setGeometry(QApplication::desktop()->geometry());
+ this->showFullScreen();
+ this->activateWindow();
+ this->raise();
+
+ // grabbing of keyboard and mouse
+ XGrabKeyboard(_sysdep->dpy, DefaultRootWindow(_sysdep->dpy) , false, GrabModeAsync, GrabModeAsync, CurrentTime);
+ XGrabPointer(_sysdep->dpy, DefaultRootWindow(_sysdep->dpy) , false, 0, GrabModeAsync, GrabModeAsync, None, 0, CurrentTime);
+
+ _locked = true;
+ return true;
+}
+
+bool BlankScreen::unlock()
+{
+ this->hide();
+ if (!_locked)
+ return true;
+ if (_sysdep->dpy == NULL)
+ return false;
+
+ // ungrabbing of keyboard and mouse
+ XUngrabPointer(_sysdep->dpy, CurrentTime);
+ XUngrabKeyboard(_sysdep->dpy, CurrentTime);
+
+ XFlush(_sysdep->dpy);
+
+ _locked = false;
+ return true;
+}
diff --git a/src/client/util/util.cpp b/src/client/util/util.cpp
new file mode 100644
index 0000000..9dcbebb
--- /dev/null
+++ b/src/client/util/util.cpp
@@ -0,0 +1,25 @@
+/*
+ * Util.cpp
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#include "util.h"
+#include <QSettings>
+
+namespace Util
+{
+//#
+//#
+QDir settingsDir()
+{
+ USER_SETTINGS(settings);
+ QFileInfo fi(settings.fileName());
+ QDir path(fi.path());
+ return path;
+}
+//#
+//#
+}
+
diff --git a/src/client/util/util.h b/src/client/util/util.h
new file mode 100644
index 0000000..82248c0
--- /dev/null
+++ b/src/client/util/util.h
@@ -0,0 +1,23 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+// Helper for getting a settings object in various places, so if you ever change the organization, location,
+// file format or anything, you won't have to edit in 100 places.
+// Use like this:
+// USER_SETTINGS(settings)
+// settings.value("somekey")
+#define USER_SETTINGS(name) QSettings name (QSettings::IniFormat, QSettings::UserScope, "openslx", "pvs2client")
+#define SYSTEM_SETTINGS(name) QSettings name (QSettings::IniFormat, QSettings::SystemScope, "openslx", "pvs2client")
+
+#include <QDir>
+
+namespace Util
+{
+//#
+//#
+QDir settingsDir();
+//#
+//#
+}
+
+#endif /* UTIL_H_ */
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_ */