[X2Go-Commits] [x2gokdriveclient] 01/01: Support for data transfer over UDP

git-admin at x2go.org git-admin at x2go.org
Wed Nov 30 16:56:10 CET 2022


This is an automated email from the git hooks/post-receive script.

x2go pushed a commit to branch feature/udp-support
in repository x2gokdriveclient.

commit c6d6a60ce027cf6779da369c358f68c7d4351bbe
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date:   Wed Nov 30 09:55:59 2022 -0600

    Support for data transfer over UDP
---
 client.cpp      | 953 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 client.h        | 109 ++++++-
 displayarea.cpp |  23 +-
 3 files changed, 942 insertions(+), 143 deletions(-)

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

--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gokdriveclient.git


More information about the x2go-commits mailing list