You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/krdc/vnc/threads.cpp

393 lines
9.5 KiB

/***************************************************************************
threads.cpp - threads
-------------------
begin : Thu May 09 17:01:44 CET 2002
copyright : (C) 2002 by Tim Jansen
email : tim@tjansen.de
***************************************************************************/
/***************************************************************************
* *
* 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 <tqcstring.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_INCRRTQ_WAIT_PERIOD = 110;
static const int MOUSEPRESS_TQUEUE_SIZE = 5;
static const int MOUSEMOVE_TQUEUE_SIZE = 3;
static const int KEY_TQUEUE_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;
TQApplication::postEvent(m_view, new StatusChangeEvent(s));
}
void ControllerThread::sendFatalError(ErrorCode s) {
m_quitFlag = true;
TQApplication::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;
}
TQApplication::postEvent(m_view,
new ScreenResizeEvent(si.framebufferWidth,
si.framebufferHeight));
m_wthread.queueUpdateRequest(TQRegion(TQRect(0,0,si.framebufferWidth,
si.framebufferHeight)));
TQApplication::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(TQString())
{
writerThread = this;
m_lastIncrUpdate.start();
}
bool WriterThread::sendIncrementalUpdateRequest() {
m_lastIncrUpdate.restart();
return SendIncrementalFramebufferUpdateRequest();
}
bool WriterThread::sendUpdateRequest(const TQRegion &region) {
TQMemArray<TQRect> 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 TQValueList<InputEvent> &events) {
TQValueList<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 TQRegion &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_TQUEUE_SIZE) {
m_lock.unlock();
return;
}
if ((m_lastMouseEvent.buttons == buttonMask) &&
(m_mouseEventNum >= MOUSEMOVE_TQUEUE_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_TQUEUE_SIZE) {
m_lock.unlock();
return;
}
m_keyEventNum++;
m_inputEvents.push_back(e);
m_waiter.wakeAll();
m_lock.unlock();
}
void WriterThread::queueClientCut(const TQString &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;
TQRegion updateRegionRQ;
TQValueList<InputEvent> inputEvents;
TQString 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_INCRRTQ_WAIT_PERIOD : MAXIMUM_WAIT_PERIOD))
m_incrementalUpdateRQ = true;
m_lock.unlock();
}
else {
m_incrementalUpdateRQ = false;
m_incrementalUpdateAnnounced = false;
m_updateRegionRQ = TQRegion();
m_inputEvents.clear();
m_keyEventNum = 0;
m_mouseEventNum = 0;
m_clientCut = TQString();
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()) {
TQCString 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;
TQApplication::postEvent(m_view, new FatalErrorEvent(s));
}