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