diff options
Diffstat (limited to 'krdc/vnc/threads.cpp')
-rw-r--r-- | krdc/vnc/threads.cpp | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/krdc/vnc/threads.cpp b/krdc/vnc/threads.cpp new file mode 100644 index 00000000..fe5a1d62 --- /dev/null +++ b/krdc/vnc/threads.cpp @@ -0,0 +1,392 @@ +/*************************************************************************** + threads.cpp - threads + ------------------- + begin : Thu May 09 17:01:44 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kvncview.h" + +#include <kdebug.h> +#include <kapplication.h> + +#include "vncviewer.h" +#include "threads.h" + +#include <qcstring.h> + +// Maximum idle time for writer thread in ms. When it timeouts, it will request +// another incremental update. Must be smaller than the timeout of the server +// (krfb's is 20s). +static const int MAXIMUM_WAIT_PERIOD = 8000; + +// time to postpone incremental updates that have not been requested explicitly +static const int POSTPONED_INCRRQ_WAIT_PERIOD = 110; + +static const int MOUSEPRESS_QUEUE_SIZE = 5; +static const int MOUSEMOVE_QUEUE_SIZE = 3; +static const int KEY_QUEUE_SIZE = 8192; + + +ControllerThread::ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag) : + m_view(v), + m_status(REMOTE_VIEW_CONNECTING), + m_wthread(wt), + m_quitFlag(quitFlag), + m_desktopInitialized(false) +{ +} + +void ControllerThread::changeStatus(RemoteViewStatus s) { + m_status = s; + QApplication::postEvent(m_view, new StatusChangeEvent(s)); +} + +void ControllerThread::sendFatalError(ErrorCode s) { + m_quitFlag = true; + QApplication::postEvent(m_view, new FatalErrorEvent(s)); + m_wthread.kick(); +} + +/* + * Calls this from the X11 thread + */ +void ControllerThread::desktopInit() { + SetVisualAndCmap(); + ToplevelInit(); + DesktopInit(m_view->winId()); + m_desktopInitialized = true; + m_waiter.wakeAll(); +} + +void ControllerThread::kick() { + m_waiter.wakeAll(); +} + +void ControllerThread::run() { + int fd; + fd = ConnectToRFBServer(m_view->host().latin1(), m_view->port()); + if (fd < 0) { + if (fd == -(int)INIT_NO_SERVER) + sendFatalError(ERROR_NO_SERVER); + else if (fd == -(int)INIT_NAME_RESOLUTION_FAILURE) + sendFatalError(ERROR_NAME); + else + sendFatalError(ERROR_CONNECTION); + return; + } + if (m_quitFlag) { + changeStatus(REMOTE_VIEW_DISCONNECTED); + return; + } + + changeStatus(REMOTE_VIEW_AUTHENTICATING); + + enum InitStatus s = InitialiseRFBConnection(); + if (s != INIT_OK) { + if (s == INIT_CONNECTION_FAILED) + sendFatalError(ERROR_IO); + else if (s == INIT_SERVER_BLOCKED) + sendFatalError(ERROR_SERVER_BLOCKED); + else if (s == INIT_PROTOCOL_FAILURE) + sendFatalError(ERROR_PROTOCOL); + else if (s == INIT_AUTHENTICATION_FAILED) + sendFatalError(ERROR_AUTHENTICATION); + else if (s == INIT_ABORTED) + changeStatus(REMOTE_VIEW_DISCONNECTED); + else + sendFatalError(ERROR_INTERNAL); + return; + } + + QApplication::postEvent(m_view, + new ScreenResizeEvent(si.framebufferWidth, + si.framebufferHeight)); + m_wthread.queueUpdateRequest(QRegion(QRect(0,0,si.framebufferWidth, + si.framebufferHeight))); + + QApplication::postEvent(m_view, new DesktopInitEvent()); + while ((!m_quitFlag) && (!m_desktopInitialized)) + m_waiter.wait(1000); + + if (m_quitFlag) { + changeStatus(REMOTE_VIEW_DISCONNECTED); + return; + } + + changeStatus(REMOTE_VIEW_PREPARING); + + if (!SetFormatAndEncodings()) { + sendFatalError(ERROR_INTERNAL); + return; + } + + changeStatus(REMOTE_VIEW_CONNECTED); + + m_wthread.start(); + + while (!m_quitFlag) { + if ((!HandleRFBServerMessage()) && (!m_quitFlag)) { + sendFatalError(ERROR_IO); + return; + } + } + + m_quitFlag = true; + changeStatus(REMOTE_VIEW_DISCONNECTED); + m_wthread.kick(); +} + +enum RemoteViewStatus ControllerThread::status() { + return m_status; +} + + + + + +static WriterThread *writerThread; +void queueIncrementalUpdateRequest() { + writerThread->queueIncrementalUpdateRequest(); +} + +void announceIncrementalUpdateRequest() { + writerThread->announceIncrementalUpdateRequest(); +} + + +WriterThread::WriterThread(KVncView *v, volatile bool &quitFlag) : + m_quitFlag(quitFlag), + m_view(v), + m_lastIncrUpdatePostponed(false), + m_incrementalUpdateRQ(false), + m_incrementalUpdateAnnounced(false), + m_mouseEventNum(0), + m_keyEventNum(0), + m_clientCut(QString::null) +{ + writerThread = this; + m_lastIncrUpdate.start(); +} + +bool WriterThread::sendIncrementalUpdateRequest() { + m_lastIncrUpdate.restart(); + return SendIncrementalFramebufferUpdateRequest(); +} + +bool WriterThread::sendUpdateRequest(const QRegion ®ion) { + QMemArray<QRect> r = region.rects(); + for (unsigned int i = 0; i < r.size(); i++) + if (!SendFramebufferUpdateRequest(r[i].x(), + r[i].y(), + r[i].width(), + r[i].height(), False)) + return false; + return true; +} + +bool WriterThread::sendInputEvents(const QValueList<InputEvent> &events) { + QValueList<InputEvent>::const_iterator it = events.begin(); + while (it != events.end()) { + if ((*it).type == KeyEventType) { + if (!SendKeyEvent((*it).e.k.k, (*it).e.k.down ? True : False)) + return false; + } + else + if (!SendPointerEvent((*it).e.m.x, (*it).e.m.y, (*it).e.m.buttons)) + return false; + it++; + } + return true; +} + +void WriterThread::queueIncrementalUpdateRequest() { + m_lock.lock(); + m_incrementalUpdateRQ = true; + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::announceIncrementalUpdateRequest() { + m_lock.lock(); + m_incrementalUpdateAnnounced = true; + m_lock.unlock(); +} + + +void WriterThread::queueUpdateRequest(const QRegion &r) { + m_lock.lock(); + m_updateRegionRQ += r; + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueMouseEvent(int x, int y, int buttonMask) { + InputEvent e; + e.type = MouseEventType; + e.e.m.x = x; + e.e.m.y = y; + e.e.m.buttons = buttonMask; + + m_lock.lock(); + if (m_mouseEventNum > 0) { + if ((e.e.m.x == m_lastMouseEvent.x) && + (e.e.m.y == m_lastMouseEvent.y) && + (e.e.m.buttons == m_lastMouseEvent.buttons)) { + m_lock.unlock(); + return; + } + if (m_mouseEventNum >= MOUSEPRESS_QUEUE_SIZE) { + m_lock.unlock(); + return; + } + if ((m_lastMouseEvent.buttons == buttonMask) && + (m_mouseEventNum >= MOUSEMOVE_QUEUE_SIZE)) { + m_lock.unlock(); + return; + } + } + + m_mouseEventNum++; + m_lastMouseEvent = e.e.m; + + m_inputEvents.push_back(e); + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueKeyEvent(unsigned int k, bool down) { + InputEvent e; + e.type = KeyEventType; + e.e.k.k = k; + e.e.k.down = down; + + m_lock.lock(); + if (m_keyEventNum >= KEY_QUEUE_SIZE) { + m_lock.unlock(); + return; + } + + m_keyEventNum++; + m_inputEvents.push_back(e); + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueClientCut(const QString &text) { + m_lock.lock(); + + m_clientCut = text; + + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::kick() { + m_waiter.wakeAll(); +} + +void WriterThread::run() { + bool incrementalUpdateRQ = false; + bool incrementalUpdateAnnounced = false; + QRegion updateRegionRQ; + QValueList<InputEvent> inputEvents; + QString clientCut; + + while (!m_quitFlag) { + m_lock.lock(); + incrementalUpdateRQ = m_incrementalUpdateRQ; + incrementalUpdateAnnounced = m_incrementalUpdateAnnounced; + updateRegionRQ = m_updateRegionRQ; + inputEvents = m_inputEvents; + clientCut = m_clientCut; + + if ((!incrementalUpdateRQ) && + (updateRegionRQ.isNull()) && + (inputEvents.size() == 0) && + (clientCut.isNull())) { + if (!m_waiter.wait(&m_lock, + m_lastIncrUpdatePostponed ? + POSTPONED_INCRRQ_WAIT_PERIOD : MAXIMUM_WAIT_PERIOD)) + m_incrementalUpdateRQ = true; + m_lock.unlock(); + } + else { + m_incrementalUpdateRQ = false; + m_incrementalUpdateAnnounced = false; + m_updateRegionRQ = QRegion(); + m_inputEvents.clear(); + m_keyEventNum = 0; + m_mouseEventNum = 0; + m_clientCut = QString::null; + m_lock.unlock(); + + // always send incremental update, unless + // a) a framebuffer update is done ATM and will do the request later, or + // b) the last unrequested update has been done less than 0.1s ago + // + // if the update has not been done because of b, postpone it. + if (incrementalUpdateRQ || !incrementalUpdateAnnounced) { + bool sendUpdate; + if (incrementalUpdateRQ) { + sendUpdate = true; + m_lastIncrUpdatePostponed = false; + } + else { + if (m_lastIncrUpdate.elapsed() < 100) { + sendUpdate = false; + m_lastIncrUpdatePostponed = true; + } + else { + sendUpdate = true; + m_lastIncrUpdatePostponed = false; + } + } + + if (sendUpdate) + if (!sendIncrementalUpdateRequest()) { + sendFatalError(ERROR_IO); + break; + } + } + else + m_lastIncrUpdatePostponed = false; + + if (!updateRegionRQ.isNull()) + if (!sendUpdateRequest(updateRegionRQ)) { + sendFatalError(ERROR_IO); + break; + } + if (inputEvents.size() != 0) + if (!sendInputEvents(inputEvents)) { + sendFatalError(ERROR_IO); + break; + } + if (!clientCut.isNull()) { + QCString cutTextUtf8(clientCut.utf8()); + if (!SendClientCutText(cutTextUtf8.data(), + (int)cutTextUtf8.length())) { + sendFatalError(ERROR_IO); + break; + } + } + } + } + m_quitFlag = true; +} + +void WriterThread::sendFatalError(ErrorCode s) { + m_quitFlag = true; + QApplication::postEvent(m_view, new FatalErrorEvent(s)); +} + |