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

x2go pushed a commit to branch master
in repository x2gokdriveclient.

commit 67abd98aa90cc7fc18ca17e99718b1b3e47c5fce
Author: Oleksandr Shneyder <o.shney...@phoca-gmbh.de>
Date:   Thu Dec 22 17:33:51 2022 -0600

    support for sending frames over UDP. Some improvements in detecting of 
unchanged regions.
---
 client.cpp       | 513 +++++++++++++++++++++++++++++++++++++++++++++++--------
 client.h         |  74 +++++++-
 debian/changelog |   2 +
 displayarea.cpp  |   7 +
 4 files changed, 519 insertions(+), 77 deletions(-)

diff --git a/client.cpp b/client.cpp
index 6f8cf57..cba4fe1 100644
--- a/client.cpp
+++ b/client.cpp
@@ -19,12 +19,13 @@
  */
 
 #include <QTcpSocket>
+#include <QUdpSocket>
+#include <QTime>
 #include "client.h"
 #include "displayarea.h"
 #include <QApplication>
 #include <QMessageBox>
 #include <QTimer>
-#include <QTcpSocket>
 #include <QPainter>
 #include <QImage>
 #include <QFile>
@@ -65,6 +66,50 @@
 //stderr
 
 
+DgramPacket::DgramPacket(uint16_t seq, uint16_t numOfDatagrams)
+{
+    datagrams.resize(numOfDatagrams);
+    packetSeq=seq;
+}
+
+//return vector with numbers of missing datagrams
+
+//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 +184,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 +192,7 @@ QTextStream& Client::KDRStdErr(bool dbg)
     static QTextStream nl(&nls);
     if(debug || !dbg)
     {
-        out<<KDR_ENDL;
+//         out<<KDR_ENDL;
         return out;
     }
     return nl;
@@ -166,13 +211,15 @@ Client::Client()
         initDesktopMode();
 
     clientSocket=new QTcpSocket(this);
-
-
-
     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()) );
+
+
+    udpSocket=new QUdpSocket(this);
+    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(UDPDataArrived()));
+
 #ifndef Q_OS_LINUX
     connect(QGuiApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), 
this, SLOT(slotSelectionChanged(QClipboard::Mode)));
 #endif
@@ -210,10 +257,7 @@ Client::~Client()
 
     freeMessageBuffer();
 
-    if(currentFrame)
-        delete currentFrame;
-    if(currentCursor)
-        delete currentCursor;
+    reinitCaches();
 }
 
 void Client::initDesktopMode()
@@ -348,7 +392,19 @@ void Client::slotEnableRandr()
 
 void Client::slotDisconnect()
 {
+    if(serverVersion >=8)
+    {//sending disconnect event to server
+        char evmsg[EVLENGTH]{};
+        uint32_t etype;
+        etype=DISCONNECTCLIENT;
+        memcpy(evmsg,(char*)&etype,4);
+        sendEvent(evmsg);
+    }
+
     clientSocket->close();
+    if(udpSocket->isOpen())
+        udpSocket->close();
+
 }
 
 void Client::slotDisplayFS()
@@ -569,6 +625,12 @@ void Client::parseOptions()
             KDRStdErr()<<"Disable resizing";
             continue;
         }
+        if(args[i]=="--udp-frames")
+        {
+            udpFrames=true;
+            udpHost=args[++i];
+            continue;
+        }
         if(args[i]=="--connect")
         {
             host=args[++i];
@@ -716,7 +778,7 @@ void Client::connectToServer()
     {
         hide();
     }
-    KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port<<KDR_ENDL;
+    KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port<<" over 
TCP"<<KDR_ENDL;
     clientSocket->connectToHost(host, port);
 }
 
@@ -725,11 +787,15 @@ QPixmap Client::getPixmapFromCache(uint32_t crc)
 {
     if(!frameCache.contains(crc))
     {
-        KDRStdErr()<<"GETPIXMAP: frame "<<KDR_HEX<<crc<<" not found in cache";
+        KDRStdErr()<<"GETPIXMAP: frame "<<KDR_HEX<<crc<<" not found in 
cache"<<KDR_ENDL;
         if(serverVersion<5)
-            exitOnError(tr("Frame not found in cache"));
-        else
+            return QPixmap();
+        if(serverVersion<8)
+        {
             requestCacheRebuild();
+            return QPixmap();
+        }
+        requestFrame(crc);
         return QPixmap();
     }
     return frameCache[crc];
@@ -751,18 +817,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
     {
@@ -771,7 +826,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];
@@ -779,12 +841,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],
@@ -798,18 +860,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;
@@ -877,6 +932,71 @@ void Client::setUptodate()
 
 
 
+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()
 {
     if(currentFrame)
@@ -973,7 +1093,6 @@ void Client::getCursor()
     if(currentCursor)
         delete currentCursor;
 
-
     currentCursor=new X2GoCursor(*((uint16_t*)messageBuffer+5), 
*((uint16_t*)messageBuffer+6),
                              *((uint16_t*)messageBuffer+7), 
*((uint16_t*)messageBuffer+8),
                              
*((uint32_t*)messageBuffer+5),*((uint32_t*)messageBuffer+6));
@@ -986,7 +1105,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
@@ -1105,7 +1224,6 @@ void Client::setInputSelectionData(SelectionType, 
SelectionMime mime, bool first
 }
 
 #endif
-
 void Client::getDeletedCursorsList()
 {
     //process list from messageBuffer
@@ -1115,12 +1233,14 @@ void Client::getDeletedCursorsList()
         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
+            KDRStdErr()<<"cursor not found in cache: "<<serial<<KDR_ENDL;
+            if(serverVersion>5)
+            {
                 requestCacheRebuild();
-            return;
+                return;
+            }
+            else
+                continue;
         }
         delete cursorCache[serial];
         cursorCache.remove(serial);
@@ -1288,7 +1408,7 @@ void Client::getWinUpdateBuffer()
 //                     KDRStdErr()<<"win has parent!!!!!: "<<KDR_HEX<<parId;
                     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, winType, flags);
@@ -1351,7 +1471,7 @@ void Client::getWinUpdateBuffer()
             {
                 //set new parent and remap window
                 win->setParentId(parId);
-                KDRStdErr()<<"Reparent window";
+                KDRStdErr()<<"Reparent window"<<KDR_ENDL;
             }
             if(win->getNextSibId()!=nextSibId)
             {
@@ -1373,19 +1493,14 @@ void Client::getWinUpdateBuffer()
 void Client::getDeletedFramesList()
 {
     //process list from messageBuffer
-
-//     KDRStdErr()<<"get deleted frames: "<<deletedFramesSize;
+//     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];
@@ -1479,21 +1594,22 @@ void Client::getSelection()
         compressed_size=0;
     }
 
-
     currentDataType=SELECTIONBUFFER;
     //if data is compressed, read the "compressed_size" bytes
     if(compressed_size)
+    {
         bytesLeftToRead=compressed_size;
+    }
     else
         bytesLeftToRead=selectionSize;
-    //     KDRStdErr()<<"Get Selection, is 
Clipboard"<<selectionClipboard<<selectionFormat<<"chunk 
size"<<selectionSize<<"left"<<bytesLeftToRead;
+//     KDRStdErr()<<"Get Selection, is Clipboard 
"<<selectionClipboard<<selectionFormat<<" chunk size "<<selectionSize<<" left 
"<<bytesLeftToRead<<KDR_ENDL;
     freeMessageBuffer();
 }
 
 
 void Client::getDeletedFrames()
 {
-    //get list of deleted cursors
+    //get list of deleted frames
     bytesReady=0;
     deletedFramesSize=*((uint32_t*)messageBuffer+1);
     bytesLeftToRead=deletedFramesSize * sizeof(uint32_t);
@@ -1506,7 +1622,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;
@@ -1634,9 +1749,32 @@ void Client::readDataHeader()
             getWinUpdate();
             break;
         }
+        case SRVKEEPALIVE:
+        {
+            //keepalive packet, do nothing
+            break;
+        }
+        case SRVDISCONNECT:
+        {
+            KDRStdErr()<<"Server sent disconnect notification"<<KDR_ENDL;
+            slotDisconnect();
+            break;
+        }
+        case UDPOPEN:
+        {
+            openUdpConnection();
+            break;
+        }
+        case UDPFAILED:
+        {
+            KDRStdErr()<<"Server rejected UDP connection, trying one more 
time...";
+            requestUdpFrames();
+            break;
+        }
         default:
         {
             KDRStdErr()<<"Unsupported header type: "<<data_type;
+            freeMessageBuffer();
             exitOnError(tr("Unsupported header type"));
             break;
         }
@@ -1644,9 +1782,156 @@ 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()
+{
+    qint64 available=udpSocket->pendingDatagramSize();
+
+    //         KDRStdErr()<<"Have available:"<<available<<KDR_ENDL;
+    QByteArray data;
+    data.resize(available);
+    qint64 read=udpSocket->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 ServerRepaintPacket:
+            lastSeq=serverRepaintSeq;
+            stringType="Server repaint packet";
+            list=&serverRepaintPackets;
+            break;
+    }
+    //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<<" last: 
"<<lastSeq<<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==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;
+        }
+    }
+}
+
+
+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);
+    sendEvent(evmsg);
+}
+
+void Client::UDPDataArrived()
+{
+//     KDRStdErr()<<"Got udp data"<<KDR_ENDL;
+    updateServerAlive();
+    while(((QUdpSocket*)udpSocket)->hasPendingDatagrams())
+        readDgram();
+}
+
 
 void Client::dataArrived()
 {
+    updateServerAlive();
 //     KDRStdErr()<<"Have available:"<<clientSocket->bytesAvailable();
     if(!bytesLeftToRead)
     {
@@ -1773,6 +2058,17 @@ void Client::checkServerVersion()
         KDRStdErr(false)<<"Server Version "<<serverVersion<<" doesn't support 
rootless mode. Please update the server package"<<KDR_ENDL;
         slotDisconnect();
     }
+    if(serverVersion>=8)
+    {
+        checkSrvAliveTimer=new QTimer(this);
+        connect(checkSrvAliveTimer, SIGNAL(timeout()),this, 
SLOT(slotCheckIfServerIsAlive()));
+        checkSrvAliveTimer->start(SERVERALIVETIMEOUT*1000);
+        QTimer* t=new QTimer(this);
+        connect(t, SIGNAL(timeout()), this, SLOT(slotSynchronize()));
+        t->start(5000);
+        if(udpFrames)
+            requestUdpFrames();
+    }
 }
 
 void Client::initGeometry()
@@ -1808,15 +2104,17 @@ void Client::socketDisconnected()
 
 void Client::socketError(QAbstractSocket::SocketError )
 {
-    KDRStdErr(false)<<clientSocket->errorString()<<KDR_ENDL;
-    exitOnError(clientSocket->errorString());
+    QString errStr;
+    errStr=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);
@@ -1826,16 +2124,19 @@ void Client::sendEvent(char* event)
 {
     if(!connected)
         return;
+    int ln;
     if(!clientSocket->isOpen())
-    {
         return;
-    }
-    if(clientSocket->write(event, EVLENGTH)!=EVLENGTH)
+    ln=clientSocket->write(event, EVLENGTH);
+
+    if(ln!=EVLENGTH)
     {
-        exitOnError(tr("Failed to send input event to server"));
+        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::moveEvent(QMoveEvent* )
 {
     if(rootless)
@@ -2320,6 +2621,17 @@ void Client::requestCacheRebuild()
     sendEvent(evmsg);
 }
 
+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);
+}
+
+
 void Client::reinitCaches()
 {
     KDRStdErr(false)<<"Clearing all caches"<<KDR_ENDL;
@@ -2334,6 +2646,65 @@ void Client::reinitCaches()
     currentCursor=0;
     if(!rootless)
         displayArea->repaint(0, 0, displayArea->width(), 
displayArea->height());
+    serverFramePackets.clear();
+    serverRepaintPackets.clear();
+    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);
+}
+
+void Client::requestUdpFrames()
+{
+    if(udpConnectionAttempts>=3)
+    {
+        KDRStdErr()<<"Failed to establish UDP connection, continue over 
TCP"<<KDR_ENDL;
+        return;
+    }
+    char evmsg[EVLENGTH]{};
+    uint32_t etype;
+    etype=OPENUDP;
+    memcpy(evmsg,(char*)&etype,4);
+    KDRStdErr()<<"Requesting UDP connection, attempt number 
"<<++udpConnectionAttempts<<KDR_ENDL;
+    sendEvent(evmsg);
+}
+
+void Client::openUdpConnection()
+{
+    int32_t udp_port=*((uint16_t*)messageBuffer+2);
+    int32_t tmp_cookie[8];
+    memcpy(tmp_cookie, messageBuffer+8,8*4);
+    KDRStdErr()<<"Server is listening on UDP port: "<<udp_port<<KDR_ENDL;
+    KDRStdErr(false)<<"Connecting to remote host "<<udpHost<<":"<<udp_port<<" 
over UDP"<<KDR_ENDL;
+    udpSocket->connectToHost(udpHost, udp_port);
+    if(!udpSocket->waitForConnected(3000))
+    {
+        KDRStdErr(false)<<"Warning, can't establish UDP connection"<<KDR_ENDL;
+    }
+    else
+    {
+        KDRStdErr(false)<<"UDP connection established"<<KDR_ENDL;
+        udpSocket->write((char*)tmp_cookie,8*4);
+    }
 }
 #ifdef Q_OS_WIN
 bool Client::nativeEvent(const QByteArray &eventType, void *message, long 
*result)
diff --git a/client.h b/client.h
index 146e98e..fcaf5e3 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,16 @@ 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
 #define KEEPALIVE 12
 #define CACHEREBUILD 13
 #define WINCHANGE 14
+//client is going to disconnect
+#define DISCONNECTCLIENT 15
+//ask to resend particular frame
+#define RESENDFRAME 16
+//client is requesting UDP port for frames
+#define OPENUDP 17
+
 
 #define ShiftMask (1<<0)
 #define LockMask (1<<1)
@@ -80,6 +89,21 @@ 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)
+
+
+//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
+    ServerRepaintPacket, // dgram belongs to packet with screen repaint and 
the loss can be ignored
+};
 
 enum SelectionMime{STRING,UTF_STRING,PIXMAP};
 enum WinState{UNCHANGED, DELETED, ICONIFIED};
@@ -160,8 +184,22 @@ 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;};
+    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;
@@ -170,6 +208,8 @@ class QAction;
 class QLabel;
 class ScreenIdentifier;
 class ExtWin;
+class QTcpSocket;
+class QUdpSocket;
 
 class Client : public QMainWindow
 {
@@ -191,7 +231,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 +254,8 @@ private slots:
     void socketDisconnected();
     void socketError(QAbstractSocket::SocketError socketError);
     void dataArrived();
+    void UDPDataArrived();
+    void slotSynchronize();
 
     void slotScreenAdded(QScreen* screen);
     void slotScreenRemoved(QScreen* screen);
@@ -233,19 +275,21 @@ 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};
-
+    enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, 
SERVER_VERSION, DEMANDCLIENTSELECTION,
+        REINIT, WINUPDATE, SRVKEEPALIVE, SRVDISCONNECT, CACHEFRAME, UDPOPEN, 
UDPFAILED};
     void getServerversion();
     void getClientSelection();
     void setUseRandr(bool use);
     void exitOnError(const QString& message);
     void getImageFrame();
+    void getImageFrameFromDGPacket(QByteArray data);
     void readDataHeader();
     void getFrameRegion();
     void getRegionImage();
@@ -260,6 +304,8 @@ private:
     void getWinUpdate();
     void getWinUpdateBuffer();
     void renderFrame();
+    void requestUdpFrames();
+    void openUdpConnection();
     void freeMessageBuffer();
     void setCursor();
     void sendGeometryEvent();
@@ -267,6 +313,10 @@ private:
     void reinitCaches();
     void initGeometry();
     void setDisplayImage();
+    void readDgram();
+    void requestFrame(uint32_t crc);
+    int findPacket(QList<DgramPacket*>* list, uint16_t seq);
+    void updateServerAlive();
     bool wantRepaint=false;
     bool hasUpdates=false;
 #ifndef Q_OS_LINUX
@@ -283,6 +333,7 @@ private:
     int width=800;
     int height=600;
     QString host="localhost";
+    QString udpHost="localhost";
     int port=15000;
 
 
@@ -294,6 +345,9 @@ private:
     int dispNumber=1;
     bool serverExtSelection=false;
     bool rootless=false;
+    bool udpFrames=false;
+    int udpConnectionAttempts=0;
+    time_t lastServerPacketTime=0;
     bool noresize=false;
     QString cookie;
 
@@ -322,6 +376,7 @@ private:
     Qt::WindowFlags savedFlags;
 
     QTcpSocket* clientSocket=0l;
+    QUdpSocket* udpSocket=0l;
     int bytesLeftToRead=0;
     int bytesReady=0;
     char* messageBuffer=0l;
@@ -355,10 +410,16 @@ private:
     QHash <uint32_t, QCursor*> cursorCache;
     QList <OutputChunk*> outputSelectionQueue;
     QList <ExtWin*> extWindows;
+    QList <DgramPacket*> serverFramePackets, serverRepaintPackets;
+    //last succesfully processed frame packet sequence
+    uint16_t serverFrameSeq=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;
 
@@ -380,6 +441,7 @@ private:
 protected:
     void resizeEvent(QResizeEvent*);
     void moveEvent(QMoveEvent*);
+    void closeEvent(QCloseEvent* );
 #ifdef Q_OS_WIN
     bool nativeEvent(const QByteArray &eventType, void *message, long *result);
 #endif
diff --git a/debian/changelog b/debian/changelog
index 1ef0d2f..b6be854 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -60,5 +60,7 @@ x2gokdriveclient (0.0.0.1-0x2go1) UNRELEASED; urgency=medium
       change background of display area,
       don't show window content and don't send geometry events while 
moving/resizing.
     - add --noresize option to disable size changing of the window for user.
+    - support for sending frames over UDP. Some improvements in detecting of
+      unchanged regions.
 
  -- Mike Gabriel <mike.gabr...@das-netzwerkteam.de>  Tue, 04 Jun 2019 11:10:43 
+0200
diff --git a/displayarea.cpp b/displayarea.cpp
index 3c2218a..24629fb 100644
--- a/displayarea.cpp
+++ b/displayarea.cpp
@@ -151,6 +151,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)
         {

--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on 
/srv/git/code.x2go.org/x2gokdriveclient.git
_______________________________________________
x2go-commits mailing list
x2go-commits@lists.x2go.org
https://lists.x2go.org/listinfo/x2go-commits

Reply via email to