This is an automated email from the git hooks/post-receive script. x2go pushed a change to branch feature/udp-support in repository x2gokdriveclient. at c6d6a60 Support for data transfer over UDP This branch includes the following new commits: new c6d6a60 Support for data transfer over UDP The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gokdriveclient.git
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@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