From 4a30aa26b608238eea90e7ecb97adc3b34d68f86 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 23 Apr 2014 20:30:48 +0200 Subject: Doxygen comments, removed incomplete unused event handling for VNC RW access, introduced terminateVnc method in VncWindow --- src/client/vnc/vncthread.cpp | 114 ++++++++++----- src/client/vnc/vncthread.h | 47 +----- src/client/vnc/vncwindow.cpp | 333 +++++++++++++++++++------------------------ src/client/vnc/vncwindow.h | 24 +--- 4 files changed, 227 insertions(+), 291 deletions(-) (limited to 'src') diff --git a/src/client/vnc/vncthread.cpp b/src/client/vnc/vncthread.cpp index 068a88d..fe52283 100644 --- a/src/client/vnc/vncthread.cpp +++ b/src/client/vnc/vncthread.cpp @@ -21,7 +21,13 @@ #include -// greatest common divisor +/** + * Calc greatest common divisor. + * + * @param a one number + * @param b another number + * @return greatest common divisor of a and b + */ static int gcd(int a, int b) { if (b == 0) @@ -29,6 +35,14 @@ static int gcd(int a, int b) return gcd(b, a % b); } +/** + * Initialize this VNC client connection worker thread. + * + * @param host The IP address of the VNC server we're going to connect to + * @param port The port of the VNC server + * @param passwd The password of the VNC server + * @param quality The desired quality level for the VNC stream + */ VncThread::VncThread(QString host, int port, QString passwd, int quality) : QThread(), _frameBuffer(NULL), _painter(NULL), _hasNewLocalSize(false), _run(true) { @@ -42,7 +56,6 @@ VncThread::VncThread(QString host, int port, QString passwd, int quality) : 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."); @@ -60,7 +73,16 @@ VncThread::~VncThread() delete _painter; } -// Calc matching pixel borders for both resolutions to prevent artifacts through bilinear scaling +/** + * When using image scaling, calc matching pixel borders for both + * resolutions to prevent artifacts through bilinear scaling. + * If you simply round to the nearest number when scaling, you would + * slightly stretch or shrink the image area, which leads to strange + * sub-pixel-movements of parts of the image when being updated. + * The values calculated here are used to determine a larger area around + * the area that should actually be updated, that will not cause any + * artifacts when scaling down/up. + */ void VncThread::calcScaling() { if (_localSize.isEmpty() || _localSize.width() == 0 || _localSize.height() == 0) @@ -80,6 +102,11 @@ void VncThread::calcScaling() //////////////////////////////////////////////////////////////////////////////// // Public +/** + * Tell the client that the size of the viewer window has changed. + * + * @param size The new size of the viewer window + */ void VncThread::setTargetSize(const QSize size) { if (_localSize == size) @@ -90,6 +117,16 @@ void VncThread::setTargetSize(const QSize size) _hasNewLocalSize = true; } +/** + * Worker thread's mainloop, connecting to the VNC server and handling the connection. + * The vnc client library is written in a blocking manner, so we just do everything in + * our own thread and just hand over the image updates to the vnc client window, which + * resides in the application's main thread. + * + * This thread checks if the vnc window signaled us to stop and if so, it deletes itself. + * This means that you should *never* delete this thread from anywhere. Just call VncThread::stop() + * and wait for it to delete itself. + */ void VncThread::run() { qDebug("[%s] VNC client started.", metaObject()->className()); @@ -160,6 +197,11 @@ void VncThread::run() QThread::run(); } +/** + * Get name of the VNC server's desktop. + * + * @return Name of the remote desktop + */ const QString VncThread::getDesktopName() const { if (_client == NULL || _client->desktopName == NULL) @@ -167,24 +209,16 @@ const QString VncThread::getDesktopName() const 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)); -} - +/** + * Handle update of an area of the VNC framebuffer. Will do any scaling + * if necessary and then emit the imageUpdated signal, so the VncWindow + * knows that it needs to redraw. + * + * @param x X offset of the area in the framebuffer that changed + * @param y Y offset of the area in the framebuffer that changed + * @param w width of the area in the framebuffer that changed + * @param h height of the area in the framebuffer that changed + */ void VncThread::processImageUpdate(const int x, const int y, const int w, const int h) { if (_srcStepX > 1 || _srcStepY > 1) @@ -223,14 +257,26 @@ void VncThread::processImageUpdate(const int x, const int y, const int w, const // *** callback stuff *** -// the vnc lib is requesting the connection password +/** + * Callback for the vnc client lib: The VNC server is requesting a password. + * + * @param client Struct representing the connection to the server. + * @return A strdup()ed copy of the password we'll try to connect with. + * The memory if free()d by the vnc client library. + */ 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 +/** + * Callback for the vnc client lib: The size of the remote screen has + * been changed, so we're supposed to reallocate the local frame buffer. + * + * @param client Struct representing the connection to the server. + * @return true (signaling the vnc client that we successfully allocated a buffer) + */ rfbBool VncThread::frameBufferHandler(rfbClient *client) { VncThread *t = (VncThread*)rfbClientGetClientData(client, 0); @@ -293,20 +339,18 @@ rfbBool VncThread::frameBufferHandler(rfbClient *client) return true; } +/** + * Callback for the vnc client lib: A part of the frame buffer has changed. That means we have to + * apply scaling if necessary, and then cause the vnc window to redraw the corresponding area. + * + * @param client Struct representing the connection to the server. + * @param x X offset of the area in the framebuffer that changed + * @param y Y offset of the area in the framebuffer that changed + * @param w width of the area in the framebuffer that changed + * @param h height of the area in the framebuffer that changed + */ 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 index 17e991d..2e94c2d 100644 --- a/src/client/vnc/vncthread.h +++ b/src/client/vnc/vncthread.h @@ -25,48 +25,6 @@ extern "C" #include } -/** - * 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; -}; - /** * VncThread - communicate with VNC server, scale image if necessary. * @@ -86,7 +44,6 @@ private: int _port; QString _passwd; int _quality; - QQueue _eventQueue; QPainter *_painter; QImage _img; @@ -106,21 +63,19 @@ private: 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); diff --git a/src/client/vnc/vncwindow.cpp b/src/client/vnc/vncwindow.cpp index 2f2b427..581d503 100644 --- a/src/client/vnc/vncwindow.cpp +++ b/src/client/vnc/vncwindow.cpp @@ -19,7 +19,7 @@ #include "vncthread.h" VncWindow::VncWindow(QWidget *parent) : - QDialog(parent), _vncWorker(NULL), _viewOnly(true), _buttonMask(0), _clientId(0), _redrawTimer(0) + QDialog(parent), _vncWorker(NULL), _viewOnly(true), _clientId(0), _redrawTimer(0), _tcpTimeoutTimer(0) { // } @@ -29,24 +29,80 @@ 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) { - // start thread for vnc-updates - this->onProjectionStopped(); + 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); - - _tcpTimeoutTimer = startTimer(4000); - _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?!? + 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); @@ -68,27 +124,49 @@ void VncWindow::open(const QString& host, int port, const QString& passwd, bool 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); + _tcpTimeoutTimer = startTimer(4000); + _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, Esc). We'll ignore the + * request and simply hide the window. + * + * @param e close event data + */ void VncWindow::closeEvent(QCloseEvent *e) { e->ignore(); qDebug("Closing VNC viewer window."); this->setVisible(false); - this->onProjectionStopped(); + 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); } /** - * VNC Thread successfully connected to remote end - projection will start + * 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() { @@ -96,51 +174,77 @@ void VncWindow::onProjectionStarted() _redrawTimer = startTimer(5000); } +/** + * 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() { - if(_vncWorker == NULL) - return; - - _vncWorker->blockSignals(true); - _vncWorker->stop(); - _vncWorker = NULL; - if(_redrawTimer != 0) - { - killTimer(_redrawTimer); - _redrawTimer = 0; - } - this->close(); - + this->terminateVncThread(); + this->close(); } //////////////////////////////////////////////////////////////////////////////// // Protected -void VncWindow::close() + +/** + * Request to close the window. Overriden from QDialog/QWidget. + * If the window is already hidden, this just emits a signal that + * will tell the server that we're not watching a VNC stream + * currently. Otherwise it will just call the implementation of + * the super class. + * + * @return false + */ +bool VncWindow::close() { if (this->isVisible()) - QDialog::close(); - else - emit running(false, _clientId); - + return QDialog::close(); + emit running(false, _clientId); + return false; } + +/** + * 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); - if (_vncWorker != NULL && !_vncWorker->isConnected()){ - this->onProjectionStopped(); + _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) { const QRect &r = event->rect(); @@ -148,161 +252,12 @@ void VncWindow::paintEvent(QPaintEvent *event) event->accept(); } +/** + * Called by Qt if the window is being resized. + * + * @param event the resize event + */ 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(event)); - return true; - break; - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - mouseEventHandler(static_cast(event)); - return true; - break; - case QEvent::Wheel: - wheelEventHandler(static_cast(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) - { - _clientId = 0; - this->close(); - } -} - -//removes modifier keys which have been pressed -void VncWindow::unpressModifiers() -{ - const QList keys = _modkeys.keys(); - QList::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 index a9c5910..f79c358 100644 --- a/src/client/vnc/vncwindow.h +++ b/src/client/vnc/vncwindow.h @@ -15,18 +15,10 @@ #define CLIENTVNCVIEWER_H_ #include -#include 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 @@ -41,36 +33,26 @@ protected slots: void onProjectionStopped(); void open(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId); + bool close(); 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); void timerEvent(QTimerEvent *event); - void close(); - //bool event(QEvent *event); - //bool eventFilter(QObject *obj, QEvent *event); private: VncThread *_vncWorker; bool _viewOnly; - int _buttonMask; - QMap _modkeys; int _clientId; int _redrawTimer; int _tcpTimeoutTimer; - bool eventFilter(QObject *obj, QEvent *event); - void keyPressEvent(QKeyEvent* event); - void keyEventHandler(QKeyEvent *e); - void unpressModifiers(); - void wheelEventHandler(QWheelEvent *event); - void mouseEventHandler(QMouseEvent *event); + void draw(const int x, const int y, const int w, const int h); + void terminateVncThread(); }; -- cgit v1.2.3-55-g7522