|
|
|
/***************************************************************************
|
|
|
|
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 ®ion) {
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|