/*
# 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) :
QWidget(parent), _vncWorker(NULL), _viewOnly(true), _clientId(0), _redrawTimer(0), _tcpTimeoutTimer(0)
{
//
}
VncWindow::~VncWindow()
{
//
}
////////////////////////////////////////////////////////////////////////////////
// Private
/**
* Terminates the vnc worker thread and stops all related timers.
* The thread will be signalled to stop, but we don't wait for it
* to actually terminate. All signaled of the thread are blocked, and we NULL
* our reference to it. It will finish running in a detached state and finally
* delete itself upon completion.
*/
void VncWindow::terminateVncThread()
{
if(_vncWorker == NULL)
return;
_vncWorker->blockSignals(true);
_vncWorker->stop();
_vncWorker = NULL;
if(_redrawTimer != 0)
{
killTimer(_redrawTimer);
_redrawTimer = 0;
}
if(_tcpTimeoutTimer != 0)
{
killTimer(_tcpTimeoutTimer);
_tcpTimeoutTimer = 0;
}
}
/**
* Draws given part of the current VNC frame buffer to the window.
* Gets the image from the _vncWorker thread in an unsafe way. :(
*
* @param x X offset of the region to draw
* @param y Y offset of the region to draw
* @param w width of the region to draw
* @param h height of the region to draw
*/
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
}
////////////////////////////////////////////////////////////////////////////////
// Public
/**
* Show the VNC client window and connect to the given VNC server.
* Any currently active VNC client connection is signaled to terminate,
* and a new VNC client worker thread is created for the new connection.
*
* @param host IP address of VNC server to connect to
* @param port Port of VNC server to connect to
* @param passwd (view only) password of VNC server to connect to
* @param ro currently unused
* @param fullscreen display VNC image in fullscreen mode
* @param caption caption of window (only visible if not running in fullscreen mode)
* @param clientId the ID of the client we're connecting to (echoed back to server, not used locally)
*/
void VncWindow::open(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId, const QByteArray& rawThumb)
{
this->terminateVncThread();
_clientId = clientId;
_vncWorker = new VncThread(host, port, passwd, 1);
connect(_vncWorker, SIGNAL(projectionStopped()), this, SLOT(onProjectionStopped()), Qt::QueuedConnection);
connect(_vncWorker, SIGNAL(projectionStarted()), this, SLOT(onProjectionStarted()), Qt::QueuedConnection);
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);
setWindowTitle(caption);
setAttribute(Qt::WA_OpaquePaintEvent);
_remoteThumb.loadFromData(rawThumb);
if (fullscreen)
{
setWindowFlags(Qt::WindowStaysOnTopHint);
showFullScreen();
activateWindow();
raise();
}
else
{
resize(800, 600);
showNormal();
}
this->repaint();
this->show();
_vncWorker->setTargetSize(this->size());
_tcpTimeoutTimer = startTimer(10000);
_vncWorker->start(QThread::LowPriority);
}
/**
* Called by Qt if the window is requested to be closed. This can either happen
* through code (this->close()), or a keypress (Alt-F4). As long as
* Qt::WA_DeleteOnClose is not set the QWidget gets just hidden and not quit.
* See http://qt-project.org/doc/qt-4.8/qcloseevent.html#details for more
* details.
*
* @param e close event data
*/
void VncWindow::closeEvent(QCloseEvent *e)
{
qDebug("Closing VNC viewer window.");
this->terminateVncThread();
emit running(false, _clientId);
}
////////////////////////////////////////////////////////////////////////////////
// Slots
/**
* Triggered by imageUpdate signal from the _vncWorker thread telling
* that the given region of the image changed. Simply repaints the
* given area of the window. Coordinates have already been scaled
* accordingly.
*
* @param x X offset of the region to update
* @param y Y offset of the region to update
* @param w width of the region to update
* @param h height of the region to update
*/
void VncWindow::onUpdateImage(const int x, const int y, const int w, const int h)
{
this->repaint(x, y, w, h);
}
/**
* Triggered by _vncWorker after successfully connecting to the VNC server.
* This emits a signal that will eventually lead to the ServerConnection
* telling the server that we're now watching another client.
*/
void VncWindow::onProjectionStarted()
{
_remoteThumb = QPixmap();
if (_tcpTimeoutTimer != 0) {
killTimer(_tcpTimeoutTimer);
_tcpTimeoutTimer = 0;
}
emit running(true, _clientId);
_redrawTimer = startTimer(3000);
}
/**
* Triggered by _vncWorker when the connection to the VNC server is lost.
* We'll terminate the thread (detached) and close the window. A signal
* telling the server that we're not watching a VNC stream anymore will
* eventually be emited by the closeEvent.
*/
void VncWindow::onProjectionStopped()
{
this->terminateVncThread();
this->close();
}
////////////////////////////////////////////////////////////////////////////////
// Protected
/**
* Called when a Qt timer fires.
*
* redrawTimer: Redraw whole viewer window.
*
* tcpTimeoutTimer: Check if we're connected, close window if not.
*
* @param event the timer event
*/
void VncWindow::timerEvent(QTimerEvent *event)
{
if (event->timerId() == _redrawTimer)
{
killTimer(_redrawTimer);
_redrawTimer = 0;
if (this->isVisible())
this->repaint();
}
else if (event->timerId() == _tcpTimeoutTimer)
{
killTimer(_tcpTimeoutTimer);
_tcpTimeoutTimer = 0;
if (_vncWorker != NULL && !_vncWorker->isConnected())
{
this->close();
}
}
else
killTimer(event->timerId());
}
/**
* Called by Qt when a part of the window should be redrawn. This can either
* be caused by Qt (or the underlying graphical subsystem), or by
* an explicit call to QWidget::repaint()
*
* @param event the paint event, containing data like location and
* size area that should be repainted
*/
void VncWindow::paintEvent(QPaintEvent *event)
{
if (_vncWorker == NULL || !_vncWorker->isConnected())
{
QPainter painter(this);
if (!_remoteThumb.isNull() && _remoteThumb.height() > 0)
{
painter.drawPixmap(0, 0, this->width(), this->height(), _remoteThumb);
}
else
{
painter.fillRect(event->rect(), QColor(60, 63, 66));
}
QFontInfo fi = painter.fontInfo();
painter.setPen(QColor(200, 100, 10));
painter.setFont(QFont(fi.family(), 28, fi.weight(), fi.italic()));
painter.drawText(this->contentsRect(), Qt::AlignCenter, tr("Connecting..."));
}
else
{
const QRect &r = event->rect();
this->draw(r.left(), r.top(), r.width(), r.height());
}
event->accept();
}
/**
* Called by Qt if the window is being resized.
*
* @param event the resize event
*/
void VncWindow::resizeEvent(QResizeEvent* event)
{
if (_vncWorker != NULL)
{
_vncWorker->setTargetSize(event->size());
}
this->repaint();
}