[X2Go-Commits] [x2gokdriveclient] 01/01: Support for data transfer over UDP
git-admin at x2go.org
git-admin at x2go.org
Wed Nov 30 16:56:10 CET 2022
This is an automated email from the git hooks/post-receive script.
x2go pushed a commit to branch feature/udp-support
in repository x2gokdriveclient.
commit c6d6a60ce027cf6779da369c358f68c7d4351bbe
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date: Wed Nov 30 09:55:59 2022 -0600
Support for data transfer over UDP
---
client.cpp | 953 +++++++++++++++++++++++++++++++++++++++++++++++++-------
client.h | 109 ++++++-
displayarea.cpp | 23 +-
3 files changed, 942 insertions(+), 143 deletions(-)
diff --git a/client.cpp b/client.cpp
index 34041bc..4c3b547 100644
--- a/client.cpp
+++ b/client.cpp
@@ -19,6 +19,8 @@
*/
#include <QTcpSocket>
+#include <QUdpSocket>
+#include <QTime>
#include "client.h"
#include "displayarea.h"
#include <QApplication>
@@ -65,6 +67,61 @@
//stderr
+DgramPacket::DgramPacket(uint16_t seq, uint16_t numOfDatagrams)
+{
+ datagrams.resize(numOfDatagrams);
+ packetSeq=seq;
+}
+
+//return vector with numbers of missing datagrams
+QVector<uint16_t> DgramPacket::getMissingDatagrams()
+{
+ QVector<uint16_t> dgs;
+ for(int i=0; i< datagrams.size(); ++i)
+ {
+ if(datagrams[i].isEmpty())
+ dgs.append(i);
+ }
+ return dgs;
+}
+
+
+//return true if the packet is complete after adding datagram
+bool DgramPacket::addDgram(QByteArray dgram)
+{
+ uint16_t dgSeq=*((uint16_t*)dgram.constData()+4);
+ //never should happen
+ if(dgSeq>=datagrams.size())
+ {
+ KDRStdErr()<<"Wrong DGRAM seq number "<<dgSeq<<" from packet "<<*((uint16_t*)data.constData()+2)<<KDR_ENDL;
+ return false;
+ }
+ if(!datagrams[dgSeq].isEmpty())
+ {
+ KDRStdErr()<<"Duplicate DGRAM seq number "<<dgSeq<<" from packet "<<*((uint16_t*)data.constData()+2)<<KDR_ENDL;
+ return false;
+ }
+ datagrams[dgSeq]=dgram;
+ int dataSize=0;
+ for(int i=0; i< datagrams.size(); ++i)
+ {
+ if(datagrams[i].isEmpty())
+ return false;
+ dataSize+=(datagrams[i].size()-SRVDGRAMHEADERSIZE);
+ }
+ //packet is complete
+ for(int i=0; i< datagrams.size(); ++i)
+ {
+ //remove datagram header
+ datagrams[i].remove(0,SRVDGRAMHEADERSIZE);
+ data.append(datagrams[i]);
+ }
+ //packet is ready
+ complete=true;
+ return true;
+}
+
+
X2GoCursor::X2GoCursor(uint16_t width, uint16_t height, uint16_t xhot, uint16_t yhot, uint32_t serialNumber, uint32_t dataSize)
{
this->width=width;
@@ -139,7 +196,7 @@ QString Client::QSizeToStr(const QSizeF& sz)
return str;
}
-QTextStream& Client::KDRStdErr(bool dbg)
+QTextStream& Client::KDRStdErrFunc(bool dbg)
{
static QTextStream out(stderr);
static QString nls;
@@ -147,7 +204,7 @@ QTextStream& Client::KDRStdErr(bool dbg)
static QTextStream nl(&nls);
if(debug || !dbg)
{
- out<<KDR_ENDL;
+// out<<KDR_ENDL;
return out;
}
return nl;
@@ -170,14 +227,25 @@ Client::Client()
((QGuiApplication*)QGuiApplication::instance())->setQuitOnLastWindowClosed(false);
}
- clientSocket=new QTcpSocket(this);
+ if(connectionType==TCP)
+ {
+ clientSocket=new QTcpSocket(this);
+ connect((QTcpSocket*)clientSocket, SIGNAL(connected()), this, SLOT(socketConnected()));
+ connect((QTcpSocket*)clientSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+ connect((QTcpSocket*)clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
+ connect((QTcpSocket*)clientSocket, SIGNAL(readyRead()), this, SLOT(dataArrived()) );
+ }
+ else
+ {
+ clientSocket=new QUdpSocket(this);
+ connect((QUdpSocket*)clientSocket, SIGNAL(connected()), this, SLOT(socketConnected()));
+ connect((QUdpSocket*)clientSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+ connect((QUdpSocket*)clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
+ connect((QUdpSocket*)clientSocket, SIGNAL(readyRead()), this, SLOT(dataArrived()) );
+ }
- connect(clientSocket, SIGNAL(connected()), this, SLOT(socketConnected()));
- connect(clientSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
- connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
- connect(clientSocket, SIGNAL(readyRead()), this, SLOT(dataArrived()) );
#ifndef Q_OS_LINUX
connect(QGuiApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), this, SLOT(slotSelectionChanged(QClipboard::Mode)));
#endif
@@ -210,10 +278,7 @@ Client::~Client()
freeMessageBuffer();
- if(currentFrame)
- delete currentFrame;
- if(currentCursor)
- delete currentCursor;
+ reinitCaches();
}
void Client::initDesktopMode()
@@ -337,7 +402,20 @@ void Client::slotEnableRandr()
void Client::slotDisconnect()
{
- clientSocket->close();
+ if(serverVersion >=8)
+ {//sending disconnect event to server
+ char evmsg[EVLENGTH]{};
+ uint32_t etype;
+ etype=DISCONNECTCLIENT;
+ memcpy(evmsg,(char*)&etype,4);
+ sendEvent(evmsg,4);
+ }
+
+ if(connectionType==TCP)
+ ((QTcpSocket*)clientSocket)->close();
+ else
+ ((QUdpSocket*)clientSocket)->close();
+
}
void Client::slotDisplayFS()
@@ -476,7 +554,10 @@ void Client::setFS(int screenNumber)
void Client::slotSetupMenu()
{
- menu->setEnabled(clientSocket->isOpen());
+ if(connectionType==TCP)
+ menu->setEnabled(((QTcpSocket*)clientSocket)->isOpen());
+ else
+ menu->setEnabled(((QUdpSocket*)clientSocket)->isOpen());
if(useRandr)
{
actRandr->setChecked(true);
@@ -695,8 +776,16 @@ void Client::connectToServer()
{
// setWindowTitle("X2GO SESSION");
- KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port<<KDR_ENDL;
- clientSocket->connectToHost(host, port);
+ if(connectionType==TCP)
+ {
+ KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port<<" over TCP"<<KDR_ENDL;
+ ((QTcpSocket*)clientSocket)->connectToHost(host, port);
+ }
+ else
+ {
+ KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port<<" over UDP"<<KDR_ENDL;
+ ((QUdpSocket*)clientSocket)->connectToHost(host, port);
+ }
}
@@ -704,11 +793,12 @@ QPixmap Client::getPixmapFromCache(uint32_t crc)
{
if(!frameCache.contains(crc))
{
- KDRStdErr()<<"GETPIXMAP: frame "<<KDR_HEX<<crc<<" not found in cache";
- if(serverVersion<5)
+ KDRStdErr()<<"GETPIXMAP: frame "<<KDR_HEX<<crc<<" not found in cache"<<KDR_ENDL;
+/* if(serverVersion<5)
exitOnError(tr("Frame not found in cache"));
else
- requestCacheRebuild();
+ requestCacheRebuild();*/
+ requestFrame(crc);
return QPixmap();
}
return frameCache[crc];
@@ -735,18 +825,7 @@ void Client::renderFrame()
this->resize(currentFrame->width, currentFrame->height);
}
displayImage=QImage(currentFrame->width, currentFrame->height, QImage::Format_RGB32);
-/* dispImagePainter.begin(&displayImage);
- dispImagePainter.drawPixmap(0,0,currentFrame->regions[0]->pix);
- dispImagePainter.end();*/
-// saveDispImage("/tmp/dispInit.png");
- }
-// else
-// {
-// dispImagePainter.begin(&displayImage);
-// dispImagePainter.drawPixmap(currentFrame->x,currentFrame->y,currentFrame->regions[0]->pix);
-// dispImagePainter.end();
-// // saveDispImage("/tmp/dispUpdate.png");
-// }
+ }
}
else
{
@@ -755,7 +834,14 @@ void Client::renderFrame()
QPainter painter;
//We have new Image. We need to create a Pixmap and add to cache
QPixmap pix(currentFrame->width, currentFrame->height);
- painter.begin(&pix);
+ if(pix.isNull())
+ {
+ KDRStdErr()<<"Error allocating new pixmap: "<<currentFrame->width<<"x"<<currentFrame->height<<KDR_ENDL;
+ }
+ if(!painter.begin(&pix))
+ {
+ KDRStdErr()<<"Error painting new image "<<currentFrame->width<<"x"<<currentFrame->height<<KDR_ENDL;
+ }
for(int i=0;i<currentFrame->regions.size();++i)
{
FrameRegion* reg=currentFrame->regions[i];
@@ -763,12 +849,12 @@ void Client::renderFrame()
{
if(!frameCache.contains(reg->source_crc))
{
- KDRStdErr()<<"region "<<KDR_HEX<<reg->source_crc<<" not found in cache";
- if(serverVersion<5)
- exitOnError(tr("region not found in cache"));
- else
- requestCacheRebuild();
- return;
+ KDRStdErr()<<"-------------------------region "<<KDR_HEX<<reg->source_crc<<" not found in cache, fill with display image"<<KDR_ENDL;
+ painter.drawImage(reg->x,reg->y,displayImage, reg->x+currentFrame->x,reg->y+currentFrame->y,reg->width, reg->height);
+ }
+ else
+ {
+// KDRStdErr()<<"Got region from cache "<<reg->source_crc<<KDR_ENDL;
}
//KDRStdErr()<<"REG:"<<reg->x<<reg->y<<reg->width<< reg->height<<"SOURCE:"<<reg->source_x<<reg->source_y;
painter.drawPixmap(reg->x,reg->y,reg->width, reg->height, frameCache[reg->source_crc],
@@ -782,18 +868,11 @@ void Client::renderFrame()
}
painter.end();
frameCache.insert(currentFrame->crc, pix);
+// KDRStdErr()<<"Add to cache: "<<currentFrame->crc<<KDR_ENDL;
int frameSize=pix.width()*pix.height()*pix.depth()/8;
cacheSize+=frameSize;
frameCount++;
-// saveDispImage("/tmp/dispDraw.png");
- /*KDRStdErr()<<"Insert in cache Frame:"<<frameCount<<dec<<frameSize<<pix.size()<<
- frameCache.count()<<"Total(MB)"<<cacheSize/(1024*1024);*/
-// QPainter dispImagePainter;
-// dispImagePainter.begin(this->getDisplayImage());
-// dispImagePainter.drawPixmap(currentFrame->x,currentFrame->y,pix);
-// dispImagePainter.end();
-
}
}
wantRepaint=true;
@@ -824,28 +903,26 @@ void Client::renderFrame()
}
setUptodate();
-
-//////put another func if works
-
- Frame* currentFrame=this->getCurrentFrame();
QPixmap pix;
if(!currentFrame->crc)
{
- // Client::KDRStdErr()<<"Draw DISPLAY PIX "<<KDR_ENDL;
pix=currentFrame->regions[0]->pix;
- // disp=true;
}
else
{
- // Client::KDRStdErr()<<"Draw PIX from cache"<<KDR_ENDL;
pix=this->getPixmapFromCache(currentFrame->crc);
+// KDRStdErr()<<"Paint from cache: "<<currentFrame->crc<<KDR_ENDL;
}
if(currentFrame->x==-1 || currentFrame->y==-1)
{
currentFrame->x=currentFrame->y=0;
}
QPainter painter;
- painter.begin(this->getDisplayImage());
+ if(!painter.begin(this->getDisplayImage()))
+ {
+ KDRStdErr()<<"Error painting display image"<<KDR_ENDL;
+ return;
+ }
painter.drawPixmap(currentFrame->x,currentFrame->y,pix);
painter.end();
}
@@ -857,6 +934,151 @@ void Client::setUptodate()
}
+void Client::processServerPacket ( QByteArray data )
+{
+ uint32_t data_type=*((uint32_t*)data.constData());
+ switch(data_type)
+ {
+ case DELETEDFRAMES:
+ {
+// KDRStdErr()<<" control packet: deleted frames "<<KDR_ENDL;
+ getDeletedFrames(data);
+ break;
+ }
+ case CURSOR:
+ {
+// KDRStdErr()<<"cursor";
+ getCursor(data);
+ break;
+ }
+ case DELETEDCURSORS:
+ {
+// KDRStdErr()<<"deleted cursors";
+ getDeletedCursors(data);
+ break;
+ }
+ case SELECTION:
+ {
+// KDRStdErr()<<"Get Selection";
+// getSelection(data);
+ break;
+ }
+ case SERVER_VERSION:
+ {
+ getServerversion(data);
+ break;
+ }
+ case DEMANDCLIENTSELECTION:
+ {
+// getClientSelection(data);
+ break;
+ }
+ case REINIT:
+ {
+ reinitCaches();
+ break;
+ }
+ case WINUPDATE:
+ {
+// getWinUpdate(data);
+ break;
+ }
+ case SRVKEEPALIVE:
+ {
+ //synchronize with server, when using UDP
+ getLastProcessedEvent(data);
+ break;
+ }
+ case SRVDISCONNECT:
+ {
+ KDRStdErr()<<"Server sent disconnect notification"<<KDR_ENDL;
+ slotDisconnect();
+ break;
+ }
+ case SRVSYNCFAILED:
+ {
+ KDRStdErr()<<"synchronization with server failed, requesting reinitialization"<<KDR_ENDL;
+ requestCacheRebuild();
+ break;
+ }
+ case RESENDEVENTS:
+ {
+ resendEvents(data);
+ break;
+ }
+ default:
+ {
+ KDRStdErr()<<"Unsupported header type: "<<data_type<<KDR_ENDL;
+ break;
+ }
+ }
+}
+
+
+void Client::getImageFrameFromDGPacket(QByteArray data)
+{
+ uint32_t winId=0;
+ const char* messageBuffer=data.constData();
+ if(*((uint32_t*)messageBuffer) == CACHEFRAME)
+ {
+ uint32_t crc=*((uint32_t*)messageBuffer+1);
+ KDRStdErr()<<"Server resent frame with crc "<<KDR_HEX<<crc<<KDR_ENDL;
+ QPixmap pix;
+ if(!pix.loadFromData((const uchar*)(messageBuffer+8),data.length()-8))
+ {
+ KDRStdErr()<<"failed to load pix from data"<<KDR_ENDL;
+ }
+ else
+ {
+ if(frameCache.contains(crc))
+ {
+ KDRStdErr()<<"Frame is already in cache"<<KDR_ENDL;
+ }
+ else
+ {
+ frameCache.insert(crc, pix);
+ }
+ }
+
+ return;
+ }
+ if(currentFrame)
+ delete currentFrame;
+
+ if(rootless)
+ winId=*((uint32_t*)messageBuffer+7);
+ currentFrame=new Frame(*((uint32_t*)messageBuffer+1), *((uint32_t*)messageBuffer+2),*((uint32_t*)messageBuffer+3),
+ *((uint32_t*)messageBuffer+4),
+ *((uint32_t*)messageBuffer+5), *((uint32_t*)messageBuffer+6), winId);
+ messageBuffer+=8*4;
+
+ for(uint i=0;i<currentFrame->numOfRegions;++i)
+ {
+ FrameRegion* region=new FrameRegion(*((uint32_t*)messageBuffer), *((uint32_t*)messageBuffer+1),*((uint32_t*)messageBuffer+2), *((uint32_t*)messageBuffer+3),
+ *((uint32_t*)messageBuffer+4), *((uint32_t*)messageBuffer+5), *((uint32_t*)messageBuffer+6),*((uint32_t*)messageBuffer+7));
+ messageBuffer+=8*4;
+ currentFrame->regions.append(region);
+ if(!region->source_crc)
+ {
+ if(currentFrame->regions.last()->pix.loadFromData((uchar*)messageBuffer, currentFrame->regions.last()->dataSize))
+ {
+ }
+ else
+ {
+ KDRStdErr()<<"=============================Image loading failed: "<<KDR_HEX<<currentFrame->crc<<" Replacing with display image"<<KDR_ENDL;
+ region->pix=QPixmap(region->width, region->height);
+ QPainter painter;
+ painter.begin(®ion->pix);
+ painter.drawImage(region->x,region->y,displayImage, region->x+currentFrame->x,region->y+currentFrame->y,region->width, region->height);
+ painter.end();
+
+ }
+ messageBuffer+=currentFrame->regions.last()->dataSize;
+ }
+ }
+ renderFrame();
+}
+
void Client::getImageFrame()
{
@@ -886,7 +1108,7 @@ void Client::getImageFrame()
-void Client::getCursorImage()
+void Client::getCursorImage(char* data)
{
//get cursor image from buffer
@@ -894,6 +1116,8 @@ void Client::getCursorImage()
// KDRStdErr()<<"got cursor image";
QCursor* cursor;
+ if(connectionType==UDP)
+ messageBuffer=data;
if(currentCursor->dataSize == (uint32_t) currentCursor->width*currentCursor->height*4)
{
// KDRStdErr()<<"get ARGB cursor";
@@ -905,7 +1129,8 @@ void Client::getCursorImage()
cursor=new QCursor();
}
cursorCache.insert(currentCursor->serialNumber, cursor);
- freeMessageBuffer();
+ if(connectionType==TCP)
+ freeMessageBuffer();
if(!rootless)
displayArea->setCursor(*cursor);
else
@@ -922,12 +1147,66 @@ void Client::setCursor()
{
}
-void Client::getServerversion()
+void Client::resendEvents(QByteArray data)
+{
+ messageBuffer=data.data();
+ uint16_t evSeq=*((uint16_t*)messageBuffer+2);
+ KDRStdErr()<<"Server missing event "<<evSeq<<KDR_ENDL;
+ if(!clientEventPackets.contains(evSeq))
+ {
+ KDRStdErr()<<"Event "<<evSeq<<" not found!";
+ requestCacheRebuild();
+ }
+ QByteArray dgram=clientEventPackets[evSeq];
+ int sent=((QUdpSocket*)clientSocket)->write(dgram);
+ if(sent != dgram.size())
+ {
+ KDRStdErr()<<"Resending event dgram failed, sent "<<sent<<" from: "<<dgram.size()<<KDR_ENDL;
+ }
+}
+
+void Client::getLastProcessedEvent(QByteArray data)
+{
+ messageBuffer=data.data();
+ uint16_t lastEvSeq=*((uint16_t*)messageBuffer+2);
+ KDRStdErr()<<"Server alive, last processed event "<<lastEvSeq<<KDR_ENDL;
+ QList<uint16_t> sequences=clientEventPackets.keys();
+ uint16_t s;
+ int32_t current_long,last_long;
+ foreach(s,sequences)
+ {
+ current_long=s;
+ last_long=lastEvSeq;
+ if(abs(current_long-last_long)>64535 && (current_long<1000) )
+ {
+ current_long+=65536;
+ }
+
+ if(abs(current_long-last_long)>64535 && (last_long<1000) )
+ {
+ last_long+=65536;
+ }
+ if(current_long<=last_long)
+ {
+ clientEventPackets.remove(s);
+ }
+ }
+}
+
+void Client::getServerversion(QByteArray data)
{
+ if(connectionType==UDP)
+ messageBuffer=data.data();
serverVersion=*((uint16_t*)messageBuffer+2);
serverExtSelection = (serverVersion>1);
KDRStdErr(false)<<"server version: "<<serverVersion<<KDR_ENDL;
+ if(serverVersion>=8)
+ {
+ checkSrvAliveTimer=new QTimer(this);
+ connect(checkSrvAliveTimer, SIGNAL(timeout()),this, SLOT(slotCheckIfServerIsAlive()));
+ checkSrvAliveTimer->start(SERVERALIVETIMEOUT*1000);
+ }
initGeometry();
}
@@ -948,11 +1227,13 @@ void Client::getClientSelection()
-void Client::getCursor()
+void Client::getCursor(QByteArray data)
{
if(currentCursor)
delete currentCursor;
+ if(connectionType==UDP)
+ messageBuffer=data.data();
currentCursor=new X2GoCursor(*((uint16_t*)messageBuffer+5), *((uint16_t*)messageBuffer+6),
*((uint16_t*)messageBuffer+7), *((uint16_t*)messageBuffer+8),
@@ -966,7 +1247,7 @@ void Client::getCursor()
//we don't have data, set cursor
if(!cursorCache.contains(currentCursor->serialNumber))
{
- KDRStdErr()<<"cursor not found: "<<currentCursor->serialNumber;
+ KDRStdErr()<<"cursor not found: "<<currentCursor->serialNumber<<KDR_ENDL;
if(serverVersion<5)
exitOnError(tr("Cursor not found in cache"));
else
@@ -985,11 +1266,18 @@ void Client::getCursor()
}
else
{
- //getCursor image
- bytesReady=0;
- bytesLeftToRead=currentCursor->dataSize;
- currentDataType=CURSORDATA;
- freeMessageBuffer();
+ if(connectionType==UDP)
+ {
+ getCursorImage(data.data()+6*4);
+ }
+ else
+ {
+ //getCursor image
+ bytesReady=0;
+ bytesLeftToRead=currentCursor->dataSize;
+ currentDataType=CURSORDATA;
+ freeMessageBuffer();
+ }
}
}
@@ -1086,26 +1374,27 @@ void Client::setInputSelectionData(SelectionType, SelectionMime mime, bool first
#endif
-void Client::getDeletedCursorsList()
+void Client::getDeletedCursorsList(char* data)
{
//process list from messageBuffer
-// KDRStdErr()<<"get deleted cursors: "<<deletedCursorsSize;
+ KDRStdErr()<<"get deleted cursors: "<<deletedCursorsSize;
+ if(connectionType==UDP)
+ {
+ messageBuffer=data;
+ }
for(uint i=0;i<deletedCursorsSize;++i)
{
uint32_t serial=*((uint32_t*)messageBuffer+i);
if(!cursorCache.contains(serial))
{
- KDRStdErr()<<"cursor not found in cache: "<<serial;
- if(serverVersion<5)
- exitOnError(tr("cursor not found in cache"));
- else
- requestCacheRebuild();
- return;
+ KDRStdErr()<<"cursor not found in cache: "<<serial<<KDR_ENDL;
+ continue;
}
delete cursorCache[serial];
cursorCache.remove(serial);
}
- freeMessageBuffer();
+ if(connectionType==TCP)
+ freeMessageBuffer();
}
ExtWin* Client::findExtWinById(uint32_t extWinId)
@@ -1247,10 +1536,10 @@ void Client::getWinUpdateBuffer()
else
{
ExtWin* parentWin=findExtWinById(parId);
- KDRStdErr()<<"win has parent!!!!!: "<<KDR_HEX<<parId;
+ KDRStdErr()<<"win has parent!!!!!: "<<KDR_HEX<<parId<<KDR_ENDL;
if(!parentWin)
{
- KDRStdErr()<<"parent Win not found in list: "<<KDR_HEX<<parId;
+ KDRStdErr()<<"parent Win not found in list: "<<KDR_HEX<<parId<<KDR_ENDL;
parentWin=(ExtWin*) this;
}
win=new ExtWin(extWinId,this, 0,flags);
@@ -1261,11 +1550,11 @@ void Client::getWinUpdateBuffer()
ExtWin* transWin=findExtWinById(transWinId);
if(!transWin)
{
- KDRStdErr()<<"trans Win not found in list: "<<KDR_HEX<<transWinId;
+ KDRStdErr()<<"trans Win not found in list: "<<KDR_HEX<<transWinId<<KDR_ENDL;
}
else
{
- KDRStdErr()<<"trans Window: "<<KDR_HEX<<transWinId;
+ KDRStdErr()<<"trans Window: "<<KDR_HEX<<transWinId<<KDR_ENDL;
// win->setParent(transWin);
win->setTransWinId(transWinId);
if(winType==WINDOW_TYPE_DIALOG)
@@ -1312,36 +1601,34 @@ void Client::getWinUpdateBuffer()
#warning inplement this
//set new parent and remap window
win->setParentId(parId);
- KDRStdErr()<<"Reparent window";
+ KDRStdErr()<<"Reparent window"<<KDR_ENDL;
}
if(win->getNextSibId()!=nextSibId)
{
#warning inplement this
// set sib and restack windows
win->setNextSibId(nextSibId);
- KDRStdErr()<<"Check if need to Restack window???";
+ KDRStdErr()<<"Check if need to Restack window???"<<KDR_ENDL;
}
}
}
freeMessageBuffer();
}
-void Client::getDeletedFramesList()
+void Client::getDeletedFramesList(char* data)
{
//process list from messageBuffer
-// KDRStdErr()<<"get deleted frames: "<<deletedFramesSize;
+ if(connectionType==UDP)
+ messageBuffer=data;
+// KDRStdErr()<<"get deleted frames: "<<deletedFramesSize<<KDR_ENDL;
for(uint i=0;i<deletedFramesSize;++i)
{
uint32_t crc=*((uint32_t*)messageBuffer+i);
if(!frameCache.contains(crc))
{
- KDRStdErr()<<"DELETING: frame not found in cache: "<<KDR_HEX<<crc;
- if(serverVersion<5)
- exitOnError(tr("frame not found in cache"));
- else
- requestCacheRebuild();
- return;
+// KDRStdErr()<<"DELETING: frame not found in cache: "<<KDR_HEX<<crc<<KDR_ENDL;
+ continue;
}
// KDRStdErr()<<"deleting frame from cache with crc"<<KDR_HEX<<crc;
QPixmap pix=frameCache[crc];
@@ -1349,7 +1636,8 @@ void Client::getDeletedFramesList()
frameCache.remove(crc);
}
- freeMessageBuffer();
+ if(connectionType==TCP)
+ freeMessageBuffer();
}
const QList<ExtWin*> Client::getSiblings(ExtWin* win)
@@ -1375,14 +1663,22 @@ void Client::getWinUpdate()
freeMessageBuffer();
}
-void Client::getDeletedCursors()
+void Client::getDeletedCursors(QByteArray data)
{
//get list of deleted cursors
- bytesReady=0;
- deletedCursorsSize=*((uint32_t*)messageBuffer+1);
- bytesLeftToRead=deletedCursorsSize * sizeof(uint32_t);
- currentDataType=CURSORLIST;
- freeMessageBuffer();
+ if(connectionType==UDP)
+ {
+ deletedCursorsSize=*((uint32_t*)data.constData()+1);
+ getDeletedCursorsList(data.data()+4*2);
+ }
+ else
+ {
+ deletedCursorsSize=*((uint32_t*)messageBuffer+1);
+ bytesReady=0;
+ bytesLeftToRead=deletedCursorsSize * sizeof(uint32_t);
+ currentDataType=CURSORLIST;
+ freeMessageBuffer();
+ }
}
void Client::getSelection()
@@ -1447,14 +1743,22 @@ void Client::getSelection()
}
-void Client::getDeletedFrames()
+void Client::getDeletedFrames(QByteArray data)
{
- //get list of deleted cursors
- bytesReady=0;
- deletedFramesSize=*((uint32_t*)messageBuffer+1);
- bytesLeftToRead=deletedFramesSize * sizeof(uint32_t);
- currentDataType=FRAMELIST;
- freeMessageBuffer();
+ //get list of deleted frames
+ if(connectionType==UDP)
+ {
+ deletedFramesSize=*((uint32_t*)data.constData()+1);
+ getDeletedFramesList(data.data()+2*4);
+ }
+ else
+ {
+ bytesReady=0;
+ deletedFramesSize=*((uint32_t*)messageBuffer+1);
+ bytesLeftToRead=deletedFramesSize * sizeof(uint32_t);
+ currentDataType=FRAMELIST;
+ freeMessageBuffer();
+ }
}
@@ -1462,7 +1766,6 @@ void Client::getRegionImage()
{
// KDRStdErr()<<"got image for region "<<currentFrame->regions.count()-1<<"from"<<currentFrame->numOfRegions;
-// currentFrame->regions.last()->pix=new QPixmap();
if(currentFrame->regions.last()->pix.loadFromData((uchar*)messageBuffer, currentFrame->regions.last()->dataSize))
{
// QString fname;
@@ -1600,9 +1903,283 @@ void Client::readDataHeader()
freeMessageBuffer();
}
+int Client::findPacket(QList< DgramPacket* >* list, uint16_t seq)
+{
+ for(int i=0;i<list->size();++i)
+ {
+ if(list->at(i)->getPacketSeq()==seq)
+ return i;
+
+ }
+ return -1;
+}
+
+
+void Client::readDgram()
+{
+ QUdpSocket* socket=(QUdpSocket*)clientSocket;
+ qint64 available=socket->pendingDatagramSize();
+
+ // KDRStdErr()<<"Have available:"<<available<<KDR_ENDL;
+ QByteArray data;
+ data.resize(available);
+ qint64 read=socket->readDatagram(data.data(),available);
+ if(read!=available)
+ {
+ KDRStdErr()<<"Read datagram failed, read "<<data.size()<<" from "<<available<<KDR_ENDL;
+ return;
+ }
+ if(data.size()<SRVDGRAMHEADERSIZE)
+ {
+ KDRStdErr()<<"DGRAM size too small"<<KDR_ENDL;
+ return;
+ }
+ uint32_t checksum=*((uint32_t*)data.constData());
+ memset(data.data(), 0, 4);
+ uint32_t crc=crc32(0L, Z_NULL, 0);
+ crc=crc32(crc,(unsigned char*)data.constData(),available);
+ if(crc!=checksum)
+ {
+ KDRStdErr()<<"DGRAM checksum failed"<<KDR_ENDL;
+ return;
+ }
+ updateServerAlive();
+
+ uint16_t packSeq=*((uint16_t*)data.constData()+2);
+ uint16_t dgInPack=*((uint16_t*)data.constData()+3);
+ // uint16_t dgSeq=*((uint16_t*)data.constData()+4);
+ uint8_t dgtype=*((uint8_t*)data.constData()+10);
+ QList<DgramPacket*> *list;
+ uint16_t lastSeq;
+ QString stringType;
+ int32_t current_long;
+ int32_t last_long;
+
+ switch (dgtype)
+ {
+ case ServerFramePacket:
+ lastSeq=serverFrameSeq;
+ stringType="Server frame packet";
+ list=&serverFramePackets;
+ break;
+ case ServerControlPacket:
+ lastSeq=serverControlSeq;
+ stringType="Server control packet";
+ list=&serverControlPackets;
+ break;
+ case ServerRepaintPacket:
+ lastSeq=serverRepaintSeq;
+ stringType="Server repaint packet";
+ list=&serverRepaintPackets;
+ break;
+ default:
+ //sync packet, process imediately
+ data.remove(0,SRVDGRAMHEADERSIZE);
+ processServerPacket(data);
+ return;
+ }
+ //this is for the case when the seq is going over the max of the uint16
+ //don't think we can lose more than 1k Packets
+ current_long=packSeq;
+ last_long=lastSeq;
+ if(abs(current_long-last_long)>64535 && (current_long<1000) )
+ {
+ current_long+=65536;
+ }
+
+ if(abs(current_long-last_long)>64535 && (last_long<1000) )
+ {
+ last_long+=65536;
+ }
+
+ if(current_long<=last_long)
+ {
+ KDRStdErr()<<"Late "<<stringType<<" arrived: "<<packSeq<<" current: "<<serverControlSeq<<KDR_ENDL;
+ return;
+ }
+ int packetInd=findPacket(list, packSeq);
+ DgramPacket* packet;
+ if(packetInd==-1)
+ {
+ //new packet
+ packet=new DgramPacket(packSeq, dgInPack);
+ list->append(packet);
+ packetInd=list->size()-1;
+ }
+ else
+ {
+// KDRStdErr()<<"packet with seq "<<packSeq<<" has ind "<<packetInd<<KDR_ENDL;
+ packet=list->at(packetInd);
+ }
+ //if true, packet is complete
+ if(packet->addDgram(data))
+ {
+ // KDRStdErr()<<"packet "<<packSeq<<" ready"<<KDR_ENDL;
+ if(dgtype==ServerControlPacket)
+ {
+ checkPacketsIntegrity(dgtype, packSeq);
+ }
+ else
+ {
+ if(dgtype==ServerFramePacket)
+ serverFrameSeq=packSeq;
+ else
+ serverRepaintSeq=packSeq;
+ getImageFrameFromDGPacket(packet->getData());
+ //delete all broken or processed packets
+ while(!list->isEmpty())
+ {
+ DgramPacket* first=list->takeFirst();
+ if(first==packet)
+ {
+ delete first;
+ break;
+ }
+ delete first;
+ }
+ }
+ }
+}
+
+bool Client::checkPacketsIntegrity(uint , uint16_t processedSeq)
+{
+ QList<DgramPacket*> *list;
+ uint16_t *lastSeq;
+ QString st;
+ uint32_t evtype;
+ QMap<uint16_t, int> *resendMap;
+
+ list=&serverControlPackets;
+ lastSeq=&serverControlSeq;
+ resendMap=&requestedControlResend;
+ st="Control";
+ evtype=RESENDSCONTROL;
+ uint16_t s=*lastSeq+1;
+ uint16_t amountOfMissedDgramsInPacket;
+ bool res=true;
+ QByteArray missingDgrams;
+
+ while(true)
+ {
+ int ind=findPacket(list,s);
+ if(ind==-1 || !list->at(ind)->isComplete())
+ {
+ //all packet or some datagrams are missed
+ res=false;
+ //check if we didn't already request this packet for resend
+ int msec=QTime::currentTime().msecsSinceStartOfDay();
+ if(resendMap->contains(s))
+ {
+// KDRStdErr()<<"Missing "<<st<<" packet "<<s<<" was requesting at "<<(*resendMap)[s]<<" now is: "<<msec<<KDR_ENDL;
+ if(((*resendMap)[s]+200 )> msec)
+ {
+ //we already requested resend of this packet
+// KDRStdErr()<<"Already requested missing "<<st<<" packet "<<s<<KDR_ENDL;
+ goto loop_fin;
+ }
+ else
+ {
+ KDRStdErr()<<"Still missing "<<st<<" packet "<<s<<" requesting again"<<KDR_ENDL;
+ }
+ }
+ //add request to the map
+ (*resendMap)[s]=msec;
+ }
+ if(ind==-1)
+ {
+ res=false;
+ //all dgrams in packet are missed
+ KDRStdErr()<<"Missing "<<st<<" packet "<<s<<" processing: "<<processedSeq<<KDR_ENDL;
+ amountOfMissedDgramsInPacket=0;
+ missingDgrams.append((const char*) &s, 2);
+ missingDgrams.append((const char*) &amountOfMissedDgramsInPacket, 2);
+ }
+ else
+ {
+ if(!list->at(ind)->isComplete())
+ {
+ res=false;
+ QVector<uint16_t> dgramSeq=list->at(ind)->getMissingDatagrams();
+ //request only dgrams from packet which are missedin packet
+ amountOfMissedDgramsInPacket=dgramSeq.size();
+ KDRStdErr()<<"Not complete "<<st<<" packet "<<s<<" missing "<<amountOfMissedDgramsInPacket<<" from "<<list->at(ind)->getNumberOfDatagrams()<<KDR_ENDL;
+ missingDgrams.append((const char*) &s, 2);
+ missingDgrams.append((const char*) &amountOfMissedDgramsInPacket, 2);
+ uint16_t seq;
+ //copy numbers of dgrams from packet which are missed
+ foreach(seq,dgramSeq)
+ {
+ missingDgrams.append((const char*) &seq, 2);
+ }
+ }
+ else
+ {
+ //the packet is complete and all packets till now are in tact
+ if(res)
+ {
+ *lastSeq=s;
+ processServerPacket(list->at(ind)->getData());
+ //if packet was requested for resend, delete it from resend map
+ if(resendMap->contains(s))
+ {
+ resendMap->remove(s);
+ }
+ //delete packet from list
+ delete list->takeAt(ind);
+ }
+ }
+ }
+loop_fin:
+ if(s==processedSeq)
+ break;
+ ++s;
+ }
+ int sent=0;
+ //max we can send in one dgram=max dgram size - client dgram header - event type
+ int maxAmountOfBytes=UDPDGRAMSIZE-CLDGRAMHEADERSIZE-4;
+ while(sent<missingDgrams.size())
+ {
+ int bytesToSend=(missingDgrams.size()-sent < maxAmountOfBytes)?(missingDgrams.size()-sent):maxAmountOfBytes;
+ QByteArray msg(4+bytesToSend, 0);
+ msg.insert(0,(char*)&evtype,4);
+ msg.insert(4, missingDgrams.constData()+sent, bytesToSend);
+ sendEvent(msg.data(),4+bytesToSend);
+ sent+=bytesToSend;
+ }
+ return res;
+}
+
+void Client::slotSynchronize()
+{
+ //not connected or server not supporting KEEPALIVE event
+ if(!connected || serverVersion<3)
+ return;
+ KDRStdErr()<<"Synchronizing: control seq:"<<serverControlSeq<<" frame seq:"<<serverFrameSeq<<" repaint seq:"<<serverRepaintSeq<<
+ " frame cache:"<<frameCache.size()<<" srv control:"<<serverControlPackets.size()<<
+ " srv frame:"<<serverFramePackets.size()<<" srv repaint:"<<serverRepaintPackets.size()<<" contr resend: "<<
+ requestedControlResend.size()<<" client event:"<<clientEventPackets.size()<<KDR_ENDL;
+
+ char evmsg[EVLENGTH]{};
+ uint32_t etype;
+ etype=KEEPALIVE;
+ memcpy(evmsg,(char*)&etype,4);
+ if(connectionType==UDP)
+ {
+ //only sending synchronization information when connected over UDP
+ memcpy(evmsg+4,(char*)&serverControlSeq,2);
+ }
+ sendEvent(evmsg,8);
+}
void Client::dataArrived()
{
+ if(connectionType==UDP)
+ {
+ while(((QUdpSocket*)clientSocket)->hasPendingDatagrams())
+ readDgram();
+ return;
+ }
+ updateServerAlive();
// KDRStdErr()<<"Have available:"<<clientSocket->bytesAvailable();
if(!bytesLeftToRead)
{
@@ -1617,7 +2194,8 @@ void Client::dataArrived()
messageBuffer=new char[bytesLeftToRead];
}
// KDRStdErr()<<"trying to read bytes:"<<bytesLeftToRead;
- int length=clientSocket->read(messageBuffer+bytesReady, bytesLeftToRead);
+ int length;
+ length=((QTcpSocket*)clientSocket)->read(messageBuffer+bytesReady, bytesLeftToRead);
bytesLeftToRead-=length;
bytesReady+=length;
@@ -1680,8 +2258,16 @@ void Client::dataArrived()
}
}
- if(clientSocket->bytesAvailable())
- dataArrived();
+ if(connectionType==TCP)
+ {
+ if(((QTcpSocket*)clientSocket)->bytesAvailable())
+ dataArrived();
+ }
+ else
+ {
+ if(((QUdpSocket*)clientSocket)->bytesAvailable())
+ dataArrived();
+ }
}
@@ -1699,7 +2285,12 @@ void Client::socketConnected()
exitOnError(tr("Wrong cookie length"));
}
KDRStdErr()<<"Sending Cookie to server"<<KDR_ENDL;
- if(clientSocket->write(cookie.toLatin1().data(), 32)!=32)
+ int ln;
+ if(connectionType==TCP)
+ ln=((QTcpSocket*)clientSocket)->write(cookie.toLatin1().data(), 32);
+ else
+ ln=((QUdpSocket*)clientSocket)->write(cookie.toLatin1().data(), 32);
+ if(ln!=32)
{
KDRStdErr(false)<<"Failed to send auth cookie to server"<<KDR_ENDL;
exitOnError(tr("Failed to send auth cookie to server"));
@@ -1708,6 +2299,19 @@ void Client::socketConnected()
else
{
KDRStdErr(false)<<"Not sending cookie to server"<<KDR_ENDL;
+ if(connectionType==UDP)
+ {
+ //if we don't have cookie, we need to send something to server to initiate connection
+ if(((QUdpSocket*)clientSocket)->write("trying UDP connection", strlen("trying UDP connection"))!=strlen("trying UDP connection"))
+ {
+ KDRStdErr(false)<<"Failed to initiate UDP connection"<<KDR_ENDL;
+ exitOnError(tr("Failed to initiate UDP connection"));
+ }
+ else
+ {
+ KDRStdErr()<<"handshake done"<<KDR_ENDL;
+ }
+ }
}
connected=true;
@@ -1715,6 +2319,13 @@ void Client::socketConnected()
// send client version
sendClientVersion();
QTimer::singleShot(2000, this, SLOT(checkServerVersion()));
+ //if connected over tcp, sending synchronization packages. In other case, just sending KEEPALIVE packets
+ if(connectionType==UDP || serverVersion >=8)
+ {
+ QTimer* t=new QTimer(this);
+ connect(t, SIGNAL(timeout()), this, SLOT(slotSynchronize()));
+ t->start(5000);
+ }
}
void Client::checkServerVersion()
@@ -1760,31 +2371,90 @@ void Client::socketDisconnected()
void Client::socketError(QAbstractSocket::SocketError )
{
- KDRStdErr(false)<<clientSocket->errorString()<<KDR_ENDL;
- exitOnError(clientSocket->errorString());
+ QString errStr;
+ if(connectionType==TCP)
+ errStr=((QTcpSocket*)clientSocket)->errorString();
+ else
+ errStr=((QUdpSocket*)clientSocket)->errorString();
+ KDRStdErr(false)<<errStr<<KDR_ENDL;
+ exitOnError(errStr);
}
-void Client::exitOnError(const QString& /*message*/)
+void Client::exitOnError(const QString& message)
{
-
// QMessageBox::critical(this,tr("Error"),message);
+ KDRStdErr()<<"Exiting on error: "<<message<<KDR_ENDL;
QApplication::closeAllWindows();
close();
QApplication::exit(-1);
}
-void Client::sendEvent(char* event)
+void Client::sendEvent(char* event, uint16_t length)
{
if(!connected)
return;
- if(!clientSocket->isOpen())
+ int ln;
+ if(connectionType==TCP)
+ {
+ if(!((QTcpSocket*)clientSocket)->isOpen())
+ return;
+ ln=((QTcpSocket*)clientSocket)->write(event, EVLENGTH);
+ }
+ else
+ {
+ QByteArray data(event,length);
+ switch(*((uint32_t*)data.constData()))
+ {
+ case CLIENTVERSION:
+ case KEEPALIVE:
+ case RESENDSCONTROL:
+ case DISCONNECTCLIENT:
+ case CLIENTSYNCFAILED:
+ case RESENDFRAME:
+ sendDgram(data, ClientSyncPacket);
+ break;
+ default:
+ sendDgram(data, ClientEventPacket);
+ }
+ return;
+ }
+
+ if(ln!=EVLENGTH)
{
+ KDRStdErr()<<"Failed to send input event to server, sent "<<ln<<" from "<<EVLENGTH<<KDR_ENDL;
+// exitOnError(tr("Failed to send input event to server"));
+ }
+}
+
+void Client::sendDgram ( QByteArray dgram, uint8_t dgtype )
+{
+ if(!connected)
+ return;
+ if(!((QUdpSocket*)clientSocket)->isOpen())
return;
+ dgram.prepend(QByteArray(CLDGRAMHEADERSIZE, (char)0));
+ if(dgtype==ClientEventPacket)
+ {
+ *((uint16_t*)dgram.data()+2)=clientEventSeq;
}
- if(clientSocket->write(event, EVLENGTH)!=EVLENGTH)
+ else
+ {
+ *((uint16_t*)dgram.data()+2)=0;
+ }
+ *((uint8_t*)dgram.data()+6)=dgtype;
+ uint32_t checksum=crc32(0L, Z_NULL, 0);
+ checksum=crc32(checksum,(const unsigned char*)dgram.constData(),dgram.size());
+ //setting checksum
+ *((uint32_t*)dgram.data())=checksum;
+ int sent=((QUdpSocket*)clientSocket)->write(dgram);
+ if(sent != dgram.size())
{
- exitOnError(tr("Failed to send input event to server"));
+ KDRStdErr()<<"Sending dgram failed, sent "<<sent<<" from: "<<dgram.size()<<KDR_ENDL;
+ }
+ if(dgtype==ClientEventPacket)
+ {
+ clientEventPackets.insert(clientEventSeq++,dgram);
}
}
@@ -1873,7 +2543,7 @@ void Client::sendGeometryEvent()
KDRStdErr()<<"not supporting more then 4 displays yet, not sending this display to server";
}
}
- sendEvent(evmsg);
+ sendEvent(evmsg, EVLENGTH);
}
@@ -1896,7 +2566,7 @@ void Client::sendClientVersion()
memcpy(evmsg+4,(char*)&version,2);
memcpy(evmsg+6,(char*)&os,2);
KDRStdErr(false)<<"Sending version: "<<version<<" OS: "<<os<<KDR_ENDL;
- sendEvent(evmsg);
+ sendEvent(evmsg,8);
}
@@ -1923,7 +2593,7 @@ void Client::changeWindow(ExtWin* win, uint8_t newState)
memcpy(evmsg+18,(char*)&h,2);
memcpy(evmsg+20,(char*)&focus,1);
memcpy(evmsg+21,(char*)&newState,1);
- sendEvent(evmsg);
+ sendEvent(evmsg,22);
// QTimer::singleShot(1,win,SLOT(update()));
}
@@ -1937,7 +2607,7 @@ void Client::requestSelectionFromServer(SelectionType sel)
etype=DEMANDSELECTION;
memcpy(evmsg,(char*)&etype,4);
memcpy(evmsg+4,(char*)&selection,2);
- sendEvent(evmsg);
+ sendEvent(evmsg,6);
}
@@ -2173,6 +2843,8 @@ void Client::sendOutputSelChunk()
{
//sending the first chunk from output selection queue
+#warning implement for udp
+ return;
if(outputSelectionQueue.isEmpty())
return;
@@ -2235,13 +2907,13 @@ void Client::sendOutputSelChunk()
uint32_t sentData=(size < EVLENGTH-headerSize)?size:EVLENGTH-headerSize;
memcpy(evmsg+headerSize,data_ptr,sentData);
- sendEvent(evmsg);
+ sendEvent(evmsg, EVLENGTH);
while(sentData<size)
{
int msg_length=(size-sentData < EVLENGTH)?size-sentData:EVLENGTH;
memcpy(evmsg, data_ptr+sentData, msg_length);
sentData+=msg_length;
- sendEvent(evmsg);
+ sendEvent(evmsg,EVLENGTH);
}
// KDRStdErr()<<"sent: "<<sentData<<"from"<<size;
delete chunk;
@@ -2269,9 +2941,20 @@ void Client::requestCacheRebuild()
etype=CACHEREBUILD;
memcpy(evmsg,(char*)&etype,4);
KDRStdErr(false)<<"Requesting cache rebuild"<<KDR_ENDL;
- sendEvent(evmsg);
+ sendEvent(evmsg,4);
+}
+
+void Client::requestFrame(uint32_t crc)
+{
+ char evmsg[EVLENGTH]{};
+ uint32_t etype;
+ etype=RESENDFRAME;
+ memcpy(evmsg,(char*)&etype,4);
+ memcpy(evmsg+4,(char*)&crc,4);
+ sendEvent(evmsg,8);
}
+
void Client::reinitCaches()
{
KDRStdErr(false)<<"Clearing all caches"<<KDR_ENDL;
@@ -2286,4 +2969,34 @@ void Client::reinitCaches()
currentCursor=0;
if(!rootless)
displayArea->repaint(0, 0, displayArea->width(), displayArea->height());
+ serverControlPackets.clear();
+ serverFramePackets.clear();
+ serverRepaintPackets.clear();
+ clientEventPackets.clear();
+ requestedControlResend.clear();
+ serverControlSeq=serverFrameSeq=serverRepaintSeq=0-1;
+ clientEventSeq=0;
+ KDRStdErr(false)<<"Done"<<KDR_ENDL;
+
+}
+
+void Client::closeEvent(QCloseEvent*)
+{
+ slotDisconnect();
+}
+
+void Client::slotCheckIfServerIsAlive()
+{
+ if(time(NULL)+SERVERALIVETIMEOUT>=lastServerPacketTime)
+ {
+ KDRStdErr()<<"Didn't recive any data from server since "<<time(NULL)-lastServerPacketTime<<" seconds, disconnecting...."<<KDR_ENDL;
+ slotDisconnect();
+ }
+}
+
+void Client::updateServerAlive()
+{
+ lastServerPacketTime=time(NULL);
+ if(checkSrvAliveTimer)
+ checkSrvAliveTimer->start(SERVERALIVETIMEOUT*1000);
}
diff --git a/client.h b/client.h
index b9c544a..f69b803 100644
--- a/client.h
+++ b/client.h
@@ -21,10 +21,13 @@
#ifndef CLIENT_H
#define CLIENT_H
+#define KDRStdErr(a) Client::KDRStdErrFunc(a)<<__FILE__<<":"<< __LINE__<<":"<< __func__<<"(): "
+
//FEATURE_VERSION is not cooresponding to actual version of client
//it used to tell server which features are supported by client
//Changes 1 - 2: supporting extended selection and sending selection on demand
-#define FEATURE_VERSION 2
+//Changes 2 - 3: support UDP protocol, sending keep alive packets
+#define FEATURE_VERSION 3
//Version of client OS for same reason
enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
@@ -58,10 +61,19 @@ enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
#define SELECTIONEVENT 9
#define CLIENTVERSION 10
#define DEMANDSELECTION 11
-//This event only sent by web client at the moment
+//This event only sent by web client without arguments to keep the connection alive
+//native client will use it whith UDP connection to synchronize the DGRAM packets
#define KEEPALIVE 12
#define CACHEREBUILD 13
#define WINCHANGE 14
+//resend missing server control dgrams
+#define RESENDSCONTROL 15
+//client is going to disconnect
+#define DISCONNECTCLIENT 16
+//synchronization with client has failed
+#define CLIENTSYNCFAILED 17
+//ask to resend particular frame
+#define RESENDFRAME 18
#define ShiftMask (1<<0)
#define LockMask (1<<1)
@@ -80,6 +92,30 @@ enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
#define HEADER_SIZE 56
#define REGION_HEADER 64
+//max size for UDP dgram
+#define UDPDGRAMSIZE 1200
+
+//UDP Server DGRAM Header - 4B checksum + 2B packet seq number + 2B amount of datagrams + 2B datagram seq number + 1B type
+#define SRVDGRAMHEADERSIZE (4+2+2+2+1)
+
+//UDP Client DGRAM Header - 4B checksum + 2B packet seq number + 1B type
+#define CLDGRAMHEADERSIZE (4+2+1)
+
+//check if server is alive every 30 seconds
+#define SERVERALIVETIMEOUT 30//sec
+
+//Types for UDP datagrams
+enum ServerDgramType{
+ ServerFramePacket, //dgram belongs to packet representing frame
+ ServerControlPacket, //dgram belongs to packet which couldn't be missed, but doesn't need to be processed in the real time
+ ServerRepaintPacket, // dgram belongs to packet with screen repaint and the loss can be ignored
+ ServerSyncPacket //dgram belongs to packet with sync request, highest priority
+};
+
+enum ClientDgramType{
+ ClientEventPacket, //Event packet, high priority
+ ClientSyncPacket //Packet with synchronization data
+};
enum SelectionMime{STRING,UTF_STRING,PIXMAP};
enum WinState{UNCHANGED, DELETED, ICONIFIED};
@@ -160,8 +196,23 @@ public:
enum SelectionType selection;
};
+class DgramPacket
+{
+public :
+ DgramPacket(uint16_t seq, uint16_t numOfDatagrams);
+ bool isComplete(){return complete;}
+ bool addDgram(QByteArray dgram);
+ int getNumberOfDatagrams(){return datagrams.size();};
+ QByteArray getData(){return data;};
+ QVector<uint16_t> getMissingDatagrams();
+ uint16_t getPacketSeq(){return packetSeq;};
+private:
+ bool complete=false;
+ QVector<QByteArray> datagrams;
+ QByteArray data;
+ uint16_t packetSeq;
+};
-class QTcpSocket;
class DisplayArea;
class QTimer;
class MenuFrame;
@@ -177,7 +228,7 @@ class Client : public QMainWindow
public:
Client();
~Client();
- void sendEvent(char *event);
+ void sendEvent(char *event, uint16_t length);
Frame* getCurrentFrame(){return currentFrame;}
bool needUpdate(){return wantRepaint;}
void setUptodate();
@@ -191,7 +242,7 @@ public:
void send_selnotify_to_server(SelectionType selection, SelectionMime mime);
int max_chunk();
static QByteArray zuncompress(const char* data, uint compressed_size, uint size);
- static QTextStream& KDRStdErr(bool dbg=true);
+ static QTextStream& KDRStdErrFunc(bool dbg=true);
static QString QRectToStr(const QRect& rec);
static QString QSizeToStr(const QSizeF& sz);
void changeWindow(ExtWin* win, uint8_t newState=UNCHANGED);
@@ -214,6 +265,7 @@ private slots:
void socketDisconnected();
void socketError(QAbstractSocket::SocketError socketError);
void dataArrived();
+ void slotSynchronize();
void slotScreenAdded(QScreen* screen);
void slotScreenRemoved(QScreen* screen);
@@ -233,28 +285,36 @@ private slots:
void slotSelectionChanged(QClipboard::Mode mode);
void requestCacheRebuild();
void checkServerVersion();
+ void slotCheckIfServerIsAlive();
public slots:
void editWindowTitle();
private:
enum{ HEADER, FRAMEREGION, REGIONDATA ,CURSORDATA, CURSORLIST, FRAMELIST, SELECTIONBUFFER, WINUPDATEBUFFER } currentDataType;
- enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, SERVER_VERSION, DEMANDCLIENTSELECTION,REINIT,WINUPDATE};
-
- void getServerversion();
+ enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, SERVER_VERSION, DEMANDCLIENTSELECTION,
+ REINIT, WINUPDATE, SRVKEEPALIVE, SRVDISCONNECT, SRVSYNCFAILED, RESENDEVENTS, CACHEFRAME};
+#warning set default to TCP
+ enum {TCP, UDP} connectionType=UDP;
+
+ void resendEvents(QByteArray data);
+ void getServerversion(QByteArray data=QByteArray());
+ void getLastProcessedEvent(QByteArray data);
void getClientSelection();
void setUseRandr(bool use);
void exitOnError(const QString& message);
void getImageFrame();
+ void getImageFrameFromDGPacket(QByteArray data);
+ void processServerPacket(QByteArray data);
void readDataHeader();
void getFrameRegion();
void getRegionImage();
- void getCursor();
- void getCursorImage();
- void getDeletedFrames();
- void getDeletedCursors();
- void getDeletedFramesList();
- void getDeletedCursorsList();
+ void getCursor(QByteArray data=QByteArray());
+ void getCursorImage(char* data=NULL);
+ void getDeletedFrames(QByteArray data=QByteArray());
+ void getDeletedCursors(QByteArray data=QByteArray());
+ void getDeletedFramesList(char* data=NULL);
+ void getDeletedCursorsList(char* data=NULL);
void getSelection();
void getSelectionBuffer();
void getWinUpdate();
@@ -266,6 +326,12 @@ private:
void setFS(int screenNumber);
void reinitCaches();
void initGeometry();
+ void readDgram();
+ void requestFrame(uint32_t crc);
+ void sendDgram(QByteArray dgram, uint8_t dgtype);
+ int findPacket(QList<DgramPacket*>* list, uint16_t seq);
+ bool checkPacketsIntegrity(uint dgramType, uint16_t processedSeq);
+ void updateServerAlive();
bool wantRepaint=false;
bool hasUpdates=false;
#ifndef Q_OS_LINUX
@@ -293,6 +359,7 @@ private:
int dispNumber=1;
bool serverExtSelection=false;
bool rootless=false;
+ time_t lastServerPacketTime=0;
QString cookie;
QString mainWndTitle;
@@ -319,7 +386,7 @@ private:
QPoint savedPosition;
Qt::WindowFlags savedFlags;
- QTcpSocket* clientSocket=0l;
+ void* clientSocket=0l;
int bytesLeftToRead=0;
int bytesReady=0;
char* messageBuffer=0l;
@@ -353,10 +420,21 @@ private:
QHash <uint32_t, QCursor*> cursorCache;
QList <OutputChunk*> outputSelectionQueue;
QList <ExtWin*> extWindows;
+ QList <DgramPacket*> serverControlPackets, serverFramePackets, serverRepaintPackets;
+ QMap <uint16_t, int> requestedControlResend;
+ QMap <uint16_t, QByteArray> clientEventPackets;
+ uint16_t clientEventSeq=0;
+ //last succesfully processed frame packet sequence
+ uint16_t serverFrameSeq=0-1;
+ //last succesfully processed control packet sequence
+ uint16_t serverControlSeq=0-1;
+ //last succesfully processed repaint packet sequence
+ uint16_t serverRepaintSeq=0-1;
int frameCount=0;
QTimer* geometryDelay=0l;
+ QTimer* checkSrvAliveTimer=0l;
QRect currentGeometry;
QRect restoreGeometry;
@@ -378,6 +456,7 @@ private:
protected:
void resizeEvent(QResizeEvent*);
void moveEvent(QMoveEvent*);
+ void closeEvent(QCloseEvent* );
};
#endif // CLIENT_H
diff --git a/displayarea.cpp b/displayarea.cpp
index 9b913ea..9bd5a8d 100644
--- a/displayarea.cpp
+++ b/displayarea.cpp
@@ -78,7 +78,7 @@ LRESULT CALLBACK LowLevelKeyboardProc(
memcpy(evmsg,(char*)&etype,4);
memcpy(evmsg+4,(char*)&mods,4);
memcpy(evmsg+8,(char*)&key,4);
- display->client->sendEvent(evmsg);
+ display->client->sendEvent(evmsg,12);
return -1;
}
return CallNextHookEx(0, nCode, wParam, lParam);
@@ -136,6 +136,13 @@ void DisplayArea::paintEvent(QPaintEvent* ev)
{
// Client::KDRStdErr()<<"Draw PIX from cache"<<KDR_ENDL;
pix=client->getPixmapFromCache(currentFrame->crc);
+ if(pix.isNull())
+ {
+ KDRStdErr()<<"Replacing with display image"<<KDR_ENDL;
+ QPixmap disp;
+ disp.convertFromImage(*client->getDisplayImage());
+ pix=disp.copy(currentFrame->x, currentFrame->y, currentFrame->width, currentFrame->height);
+ }
}
if(currentFrame->x==-1 || currentFrame->y==-1)
{
@@ -243,7 +250,7 @@ void DisplayArea::mouseMoveEvent(QMouseEvent* event)
memcpy(evmsg+4,(char*)&x,4);
memcpy(evmsg+8,(char*)&y,4);
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
// Client::KDRStdErr()<<"mouse move"<<event->x()<<event->y();
@@ -263,7 +270,7 @@ void DisplayArea::mousePressEvent(QMouseEvent* event)
memcpy(evmsg+4,(char*)&state,4);
memcpy(evmsg+8,(char*)&button,4);
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
// Client::KDRStdErr()<<"mouse press"<<event->button()<<event->buttons()<<button<<state;
}
@@ -282,7 +289,7 @@ void DisplayArea::mouseReleaseEvent(QMouseEvent* event)
memcpy(evmsg+4,(char*)&state,4);
memcpy(evmsg+8,(char*)&button,4);
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
// Client::KDRStdErr()<<"mouse release"<<event->button()<<event->buttons()<<button<<state;
}
@@ -311,7 +318,7 @@ void DisplayArea::wheelEvent(QWheelEvent* event)
memcpy(evmsg+8,(char*)&button,4);
// Client::KDRStdErr()<<etype<<state<<button;
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
etype=MOUSERELEASE;
state=X11MouseButtonsState(event->buttons());
@@ -332,7 +339,7 @@ void DisplayArea::wheelEvent(QWheelEvent* event)
memcpy(evmsg+8,(char*)&button,4);
// Client::KDRStdErr()<<etype<<state<<button;
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
}
/*
@@ -381,7 +388,7 @@ void DisplayArea::keyPressEvent(QKeyEvent* event)
memcpy(evmsg,(char*)&etype,4);
memcpy(evmsg+4,(char*)&state,4);
memcpy(evmsg+8,(char*)&key,4);
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
}
void DisplayArea::keyReleaseEvent(QKeyEvent* event)
@@ -405,7 +412,7 @@ void DisplayArea::keyReleaseEvent(QKeyEvent* event)
memcpy(evmsg+4,(char*)&state,4);
memcpy(evmsg+8,(char*)&key,4);
- client->sendEvent(evmsg);
+ client->sendEvent(evmsg,12);
}
uint32_t DisplayArea::qtModifiers2pc105(Qt::KeyboardModifiers qtModifiers)
--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gokdriveclient.git
More information about the x2go-commits
mailing list