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

git-admin at x2go.org git-admin at x2go.org
Wed Nov 30 16:46:33 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 x2gokdrive.

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

    Support for data transfer over UDP
---
 x2gokdrive.c       |    6 +-
 x2gokdriveremote.c | 1800 +++++++++++++++++++++++++++++++++++++++++-----------
 x2gokdriveremote.h |  117 +++-
 3 files changed, 1529 insertions(+), 394 deletions(-)

diff --git a/x2gokdrive.c b/x2gokdrive.c
index c81d126..fb18298 100644
--- a/x2gokdrive.c
+++ b/x2gokdrive.c
@@ -709,7 +709,11 @@ void setOutput(ScreenPtr pScreen, RROutputPtr output, RRCrtcPtr crtc, int width,
     modeInfo.height = height;
     modeInfo.hTotal =width;
     modeInfo.vTotal = height;
-    modeInfo.dotClock =0;
+    modeInfo.dotClock= ((CARD32) width * (CARD32) height *
+                         (CARD32) 60);
+    //+vSync|-hSync
+    modeInfo.modeFlags=0x0004|0x0002;
+;
 
     modeInfo.nameLength = strlen(modename);
     mode = RRModeGet(&modeInfo, modename);
diff --git a/x2gokdriveremote.c b/x2gokdriveremote.c
index 2790a66..e4d7002 100644
--- a/x2gokdriveremote.c
+++ b/x2gokdriveremote.c
@@ -164,6 +164,7 @@ void remote_handle_signal(int signum)
             }
             case RUNNING:
             {
+                send_srv_disconnect();
                 disconnect_client();
                 return;
             }
@@ -387,7 +388,14 @@ void remote_sendVersion(void)
     *((uint32_t*)buffer)=SERVERVERSION; //4B
     *((uint16_t*)buffer+2)=FEATURE_VERSION;
     EPHYR_DBG("Sending server version: %d", FEATURE_VERSION);
-    l=write(remoteVars.clientsock,buffer,56);
+    if(remoteVars.serverType==TCP)
+    {
+        l=write(remoteVars.clientsock,buffer,56);
+    }
+    else
+    {
+        l=send_packet_as_datagrams(buffer, 6, ServerSyncPacket);
+    }
     remoteVars.server_version_sent=TRUE;
 }
 
@@ -398,17 +406,34 @@ void request_selection_from_client(enum SelectionType selection)
 
     *((uint32_t*)buffer)=DEMANDCLIENTSELECTION; //4B
     *((uint16_t*)buffer+2)=(uint16_t)selection;
-    l=write(remoteVars.clientsock,buffer,56);
+    if(remoteVars.serverType==TCP)
+        l=write(remoteVars.clientsock,buffer,56);
+    else
+        l=send_packet_as_datagrams(buffer, 6, ServerControlPacket);
     EPHYR_DBG("requesting selection from client");
 }
 
 static
 int32_t send_cursor(struct cursorFrame* cursor)
 {
-    unsigned char buffer[64] = {0};
+    unsigned char* buffer;
+    unsigned char static_buffer[64]={0};
     _X_UNUSED int ln = 0;
     int l = 0;
     int sent = 0;
+    int total = 0;
+    int hdr_sz=7*4;
+
+    if(remoteVars.serverType==UDP)
+    {
+        //packet size: header size + data size
+        total=hdr_sz +cursor->size;
+        buffer=malloc(total);
+    }
+    else
+    {
+        buffer=static_buffer;
+    }
 
     *((uint32_t*)buffer)=CURSOR; //4B
 
@@ -431,17 +456,26 @@ int32_t send_cursor(struct cursorFrame* cursor)
 //     EPHYR_DBG("SENDING CURSOR %d with size %d", cursor->serialNumber, cursor->size);
 
 //    #warning check this
-    ln=write(remoteVars.clientsock,buffer,56);
-
-    while(sent<cursor->size)
+    if(remoteVars.serverType==TCP)
     {
-        l=write(remoteVars.clientsock, cursor->data+sent,((cursor->size-sent)<MAXMSGSIZE)?(cursor->size-sent):MAXMSGSIZE);
-        if(l<0)
+        ln=write(remoteVars.clientsock,buffer,56);
+
+        while(sent<cursor->size)
         {
-            EPHYR_DBG("Error sending cursor!!!!!");
-            break;
+            l=write(remoteVars.clientsock, cursor->data+sent,((cursor->size-sent)<MAXMSGSIZE)?(cursor->size-sent):MAXMSGSIZE);
+            if(l<0)
+            {
+                EPHYR_DBG("Error sending cursor!!!!!");
+                break;
+            }
+            sent+=l;
         }
-        sent+=l;
+    }
+    else
+    {
+        memcpy(buffer+hdr_sz, cursor->data, cursor->size);
+        sent=send_packet_as_datagrams(buffer, total, ServerControlPacket);
+        free(buffer);
     }
     remoteVars.data_sent+=sent;
 //    EPHYR_DBG("SENT total %d", total);
@@ -453,87 +487,136 @@ static
 int32_t send_frame(u_int32_t width, uint32_t height, uint32_t x, uint32_t y, uint32_t crc, struct frame_region* regions, uint32_t winId)
 {
     unsigned char buffer[64] = {0};
+    unsigned char* head_buffer=buffer;
+    unsigned char* data=0;
+    unsigned int header_size=8*4;
+    unsigned int region_header_size=8*4;
     _X_UNUSED int ln = 0;
     int l = 0;
     int sent = 0;
+    int i;
+    //number of datagrams
 
     uint32_t total=0;
     uint32_t numofregions=0;
 
-    for(int i=0;i<9;++i)
+    for(i=0;i<9;++i)
     {
         if(regions[i].rect.size.width && regions[i].rect.size.height)
             ++numofregions;
     }
-    *((uint32_t*)buffer)=FRAME;
-    *((uint32_t*)buffer+1)=width;
-    *((uint32_t*)buffer+2)=height;
-    *((uint32_t*)buffer+3)=x;
-    *((uint32_t*)buffer+4)=y;
-    *((uint32_t*)buffer+5)=numofregions;
-    *((uint32_t*)buffer+6)=crc;
+
+    //calculating total size of the data need to be sent
+    if(remoteVars.serverType==UDP)
+    {
+        //frame header size
+        total=header_size;
+        for(i=0;i<9;++i)
+        {
+            if(!(regions[i].rect.size.width && regions[i].rect.size.height))
+                continue;
+            //region header size
+            total+=header_size;
+            total+=regions[i].size;
+        }
+//         EPHYR_DBG("Sending frame, total size %d", total);
+        data=malloc(total);
+        memset(data,0,header_size);
+        head_buffer=data;
+    }
+
+    *((uint32_t*)head_buffer)=FRAME;
+    *((uint32_t*)head_buffer+1)=width;
+    *((uint32_t*)head_buffer+2)=height;
+    *((uint32_t*)head_buffer+3)=x;
+    *((uint32_t*)head_buffer+4)=y;
+    *((uint32_t*)head_buffer+5)=numofregions;
+    *((uint32_t*)head_buffer+6)=crc;
     if(remoteVars.rootless)
     {
-        *((uint32_t*)buffer+7)=winId;
+        *((uint32_t*)head_buffer+7)=winId;
         /*if(winId)
         {
             EPHYR_DBG("Sending frame for Window 0x%X",winId);
         }*/
     }
 
-
-//    if(numofregions)
-//        EPHYR_DBG("SENDING NEW FRAME %x", crc);
-//    else
-//        EPHYR_DBG("SENDING REFERENCE %x", crc);
-
-//    #warning check this
-    ln=write(remoteVars.clientsock, buffer,56);
-    for(int i=0;i<9;++i)
+    if(remoteVars.serverType==TCP)
+    {
+        ln=write(remoteVars.clientsock, buffer,56);
+    }
+    else
+    {
+        //increment offset on frame header size
+        head_buffer+=header_size;
+    }
+    for(i=0;i<9;++i)
     {
         if(!(regions[i].rect.size.width && regions[i].rect.size.height))
             continue;
-
-//        EPHYR_DBG("SENDING FRAME REGION %x %dx%d %d",regions[i].source_crc, regions[i].rect.size.width, regions[i].rect.size.height,
-//                  regions[i].size);
-
-        *((uint32_t*)buffer)=regions[i].source_crc;
-
-//      if(*((uint32_t*)buffer)=regions[i].source_crc)
-//          EPHYR_DBG("SENDING REFERENCE %x", *((uint32_t*)buffer)=regions[i].source_crc);
-
-        *((uint32_t*)buffer+1)=regions[i].source_coordinates.x;
-        *((uint32_t*)buffer+2)=regions[i].source_coordinates.y;
-        *((uint32_t*)buffer+3)=regions[i].rect.lt_corner.x;
-        *((uint32_t*)buffer+4)=regions[i].rect.lt_corner.y;
-        *((uint32_t*)buffer+5)=regions[i].rect.size.width;
-        *((uint32_t*)buffer+6)=regions[i].rect.size.height;
-        *((uint32_t*)buffer+7)=regions[i].size;
-
-        sent = 0;
-//        #warning check this
-        ln=write(remoteVars.clientsock, buffer, 64);
-
-        while(sent<regions[i].size)
+        //        EPHYR_DBG("SENDING FRAME REGION %x %dx%d %d",regions[i].source_crc, regions[i].rect.size.width, regions[i].rect.size.height,
+        //                  regions[i].size);
+        *((uint32_t*)head_buffer)=regions[i].source_crc;
+
+        //      if(*((uint32_t*)buffer)=regions[i].source_crc)
+        //          EPHYR_DBG("SENDING REFERENCE %x", *((uint32_t*)buffer)=regions[i].source_crc);
+
+        *((uint32_t*)head_buffer+1)=regions[i].source_coordinates.x;
+        *((uint32_t*)head_buffer+2)=regions[i].source_coordinates.y;
+        *((uint32_t*)head_buffer+3)=regions[i].rect.lt_corner.x;
+        *((uint32_t*)head_buffer+4)=regions[i].rect.lt_corner.y;
+        *((uint32_t*)head_buffer+5)=regions[i].rect.size.width;
+        *((uint32_t*)head_buffer+6)=regions[i].rect.size.height;
+        *((uint32_t*)head_buffer+7)=regions[i].size;
+
+        if(remoteVars.serverType==UDP)
         {
-            l=write(remoteVars.clientsock,regions[i].compressed_data+sent,
-                    ((regions[i].size-sent)<MAXMSGSIZE)?(regions[i].size-sent):MAXMSGSIZE);
-            if(l<0)
+            //increment offset on region header size
+            head_buffer+=region_header_size;
+            //copy region data to data
+            memcpy(head_buffer,regions[i].compressed_data, regions[i].size);
+            head_buffer+=regions[i].size;
+        }
+        else
+        {
+            sent = 0;
+            //        #warning check this
+            ln=write(remoteVars.clientsock, buffer, 64);
+
+            while(sent<regions[i].size)
             {
-                EPHYR_DBG("Error sending file!!!!!");
-                break;
+                l=write(remoteVars.clientsock,regions[i].compressed_data+sent,
+                        ((regions[i].size-sent)<MAXMSGSIZE)?(regions[i].size-sent):MAXMSGSIZE);
+                if(l<0)
+                {
+                    EPHYR_DBG("Error sending file!!!!!");
+                    break;
+                }
+                sent+=l;
             }
-            sent+=l;
+            total+=sent;
+            //        EPHYR_DBG("SENT %d",sent);
+            //
+            //        EPHYR_DBG("\ncache elements %d, cache size %lu(%dMB), connection time=%d, sent %lu(%dMB)\n",
+            //                  cache_elements, cache_size, (int) (cache_size/1024/1024),
+            //                  time(NULL)-con_start_time, data_sent, (int) (data_sent/1024/1024));
         }
-        total+=sent;
-//        EPHYR_DBG("SENT %d",sent);
-//
-//        EPHYR_DBG("\ncache elements %d, cache size %lu(%dMB), connection time=%d, sent %lu(%dMB)\n",
-//                  cache_elements, cache_size, (int) (cache_size/1024/1024),
-//                  time(NULL)-con_start_time, data_sent, (int) (data_sent/1024/1024));
-//
-
     }
+    //if proto TCP all data is sent at this moment
+    if(remoteVars.serverType==UDP)
+    {
+        if(remoteVars.compression==JPEG)
+        {
+            //if it compressed with JPEG is a frame, if not - refresh
+            total=send_packet_as_datagrams(data, total, ServerFramePacket);
+        }
+        else
+        {
+            total=send_packet_as_datagrams(data, total, ServerRepaintPacket);
+        }
+        free(data);
+    }
+
     remoteVars.data_sent+=total;
 
 //     EPHYR_DBG("SENT total %d", total);
@@ -544,8 +627,11 @@ int32_t send_frame(u_int32_t width, uint32_t height, uint32_t x, uint32_t y, uin
 static
 int send_deleted_elements(void)
 {
-    unsigned char buffer[56] = {0};
+    unsigned char* buffer;
+    unsigned char static_buffer[56] = {0};
     unsigned char* list = NULL;
+    int total = 0;
+    int hdr_sz=2*4;
 
     _X_UNUSED int ln = 0;
     int l = 0;
@@ -554,14 +640,24 @@ int send_deleted_elements(void)
     unsigned int i = 0;
     struct deleted_elem* elem = NULL;
 
+    length=remoteVars.deleted_list_size*sizeof(uint32_t);
+    if(remoteVars.serverType==UDP)
+    {
+        //packet size: header size + data size
+        total=hdr_sz + length;
+        buffer=malloc(total);
+    }
+    else
+    {
+        buffer=static_buffer;
+    }
+
+
     *((uint32_t*)buffer)=DELETED;
     *((uint32_t*)buffer+1)=remoteVars.deleted_list_size;
 
-    list=malloc(sizeof(uint32_t)*remoteVars.deleted_list_size);
+    list=malloc(length);
 
-//    #warning check this
-    ln=write(remoteVars.clientsock,buffer,56);
-//     data_sent+=48;
     while(remoteVars.first_deleted_elements)
     {
 //        EPHYR_DBG("To DELETE FRAME %x", remoteVars.first_deleted_elements->crc);
@@ -576,26 +672,40 @@ int send_deleted_elements(void)
     remoteVars.last_deleted_elements=0l;
 
 //    EPHYR_DBG("SENDING IMG length - %d, number - %d\n",length,framenum_sent++);
-    length=remoteVars.deleted_list_size*sizeof(uint32_t);
-    while(sent<length)
+    if(remoteVars.serverType==TCP)
     {
-        l=write(remoteVars.clientsock,list+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
-        if(l<0)
+        ln=write(remoteVars.clientsock,buffer,56);
+        while(sent<length)
         {
-            EPHYR_DBG("Error sending list of deleted elements!!!!!");
-            break;
+            l=write(remoteVars.clientsock,list+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
+            if(l<0)
+            {
+                EPHYR_DBG("Error sending list of deleted elements!!!!!");
+                break;
+            }
+            sent+=l;
         }
-        sent+=l;
+    }
+    else
+    {
+        memcpy(buffer+hdr_sz, list, length);
+        sent=send_packet_as_datagrams(buffer, total, ServerControlPacket);
+        free(buffer);
     }
     remoteVars.deleted_list_size=0;
+    free(list);
     return sent;
 }
 
 static
 int send_deleted_cursors(void)
 {
-    unsigned char buffer[56] = {0};
+    unsigned char* buffer;
+    unsigned char static_buffer[56] = {0};
     unsigned char* list = NULL;
+    int total = 0;
+    int hdr_sz=2*4;
+
     _X_UNUSED int ln = 0;
     int l = 0;
     int length, sent = 0;
@@ -603,13 +713,24 @@ int send_deleted_cursors(void)
     unsigned int i=0;
     struct deletedCursor* elem = NULL;
 
+    length=remoteVars.deletedcursor_list_size*sizeof(uint32_t);
+    if(remoteVars.serverType==UDP)
+    {
+        //packet size: header size + data size
+        total=hdr_sz + length;
+        buffer=malloc(total);
+    }
+    else
+    {
+        buffer=static_buffer;
+    }
+
+
     *((uint32_t*)buffer)=DELETEDCURSOR;
     *((uint32_t*)buffer+1)=remoteVars.deletedcursor_list_size;
 
-//    #warning check this
-    ln=write(remoteVars.clientsock,buffer,56);
 
-    list=malloc(sizeof(uint32_t)*remoteVars.deletedcursor_list_size);
+    list=malloc(length);
 
     while(remoteVars.first_deleted_cursor)
     {
@@ -625,17 +746,28 @@ int send_deleted_cursors(void)
     remoteVars.last_deleted_cursor=0l;
 
 //    EPHYR_DBG("Sending list from %d elements", deletedcursor_list_size);
-    length=remoteVars.deletedcursor_list_size*sizeof(uint32_t);
-    while(sent<length)
+    if(remoteVars.serverType==TCP)
     {
-        l=write(remoteVars.clientsock,list+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
-        if(l<0)
+        ln=write(remoteVars.clientsock,buffer,56);
+        while(sent<length)
         {
-            EPHYR_DBG("Error sending list of deleted cursors!!!!!");
-            break;
+            l=write(remoteVars.clientsock,list+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
+            if(l<0)
+            {
+                EPHYR_DBG("Error sending list of deleted cursors!!!!!");
+                break;
+            }
+            sent+=l;
         }
-        sent+=l;
     }
+    else
+    {
+        memcpy(buffer+hdr_sz, list, length);
+        sent=send_packet_as_datagrams(buffer, total, ServerControlPacket);
+        free(buffer);
+    }
+    free(list);
+
     remoteVars.deletedcursor_list_size=0;
     return sent;
 }
@@ -667,16 +799,40 @@ void send_reinit_notification(void)
     _X_UNUSED int l;
     *((uint32_t*)buffer)=REINIT;
     EPHYR_DBG("SENDING REINIT NOTIFICATION");
-    l=write(remoteVars.clientsock,buffer,56);
+    if(remoteVars.serverType==TCP)
+        l=write(remoteVars.clientsock,buffer,56);
+    else
+        l=send_packet_as_datagrams(buffer,4,ServerControlPacket);
 }
 
-int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, uint32_t compressed, uint32_t total)
+int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, uint32_t compressed, uint32_t total_sz)
 {
-    unsigned char buffer[56] = {0};
+
+    unsigned char* buffer;
+    unsigned char static_buffer[56]={0};
     _X_UNUSED int ln = 0;
     int l = 0;
     int sent = 0;
+    int total = 0;
+    int hdr_sz=8*4;
+
+    //if the data is compressed, send "compressed" amount of bytes
+//     EPHYR_DBG("sending chunk. total %d, chunk %d, compressed %d", total, length, compressed);
+    if(compressed)
+    {
+        length=compressed;
+    }
 
+    if(remoteVars.serverType==UDP)
+    {
+        //packet size: header size + data size
+        total=hdr_sz +length;
+        buffer=malloc(total);
+    }
+    else
+    {
+        buffer=static_buffer;
+    }
 
     *((uint32_t*)buffer)=SELECTION;    //0
     *((uint32_t*)buffer+1)=sel;        //4
@@ -685,29 +841,29 @@ int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t
     *((uint32_t*)buffer+4)=first;      //20
     *((uint32_t*)buffer+5)=last;       //24
     *((uint32_t*)buffer+6)=compressed; //28
-    *((uint32_t*)buffer+7)=total; //32
-
-
-//    #warning check this
-    ln=write(remoteVars.clientsock,buffer,56);
+    *((uint32_t*)buffer+7)=total_sz; //32
 
-    //if the data is compressed, send "compressed" amount of bytes
-//     EPHYR_DBG("sending chunk. total %d, chunk %d, compressed %d", total, length, compressed);
-    if(compressed)
-    {
-        length=compressed;
-    }
 
 
-    while(sent<length)
+    if(remoteVars.serverType==TCP)
     {
-        l=write(remoteVars.clientsock,data+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
-        if(l<0)
+        ln=write(remoteVars.clientsock,buffer,56);
+        while(sent<length)
         {
-            EPHYR_DBG("Error sending selection!!!!!");
-            break;
+            l=write(remoteVars.clientsock,data+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
+            if(l<0)
+            {
+                EPHYR_DBG("Error sending selection!!!!!");
+                break;
+            }
+            sent+=l;
         }
-        sent+=l;
+    }
+    else
+    {
+        memcpy(buffer+hdr_sz, data, length);
+        sent=send_packet_as_datagrams(buffer, total, ServerControlPacket);
+        free(buffer);
     }
     return sent;
 }
@@ -1771,6 +1927,7 @@ void *send_frame_thread (void *threadid)
             remoteVars.cache_rebuilt=FALSE;
             pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
             send_reinit_notification();
+            clean_everything();
             pthread_mutex_lock(&remoteVars.sendqueue_mutex);
         }
 
@@ -2124,13 +2281,19 @@ void disconnect_client(void)
     EPHYR_DBG("DISCONNECTING CLIENT, DOING SOME CLEAN UP");
 
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+    if(remoteVars.checkKeepAliveTimer)
+    {
+        TimerFree(remoteVars.checkKeepAliveTimer);
+        remoteVars.checkKeepAliveTimer=0;
+    }
+    if(remoteVars.sendKeepAliveTimer)
+    {
+        TimerFree(remoteVars.sendKeepAliveTimer);
+        remoteVars.sendKeepAliveTimer=0;
+    }
     remoteVars.client_connected=FALSE;
     setAgentState(SUSPENDED);
-    delete_all_windows();
-    clear_send_queue();
-    clear_frame_cache(0);
-    freeCursors();
-    clear_output_selection();
+    clean_everything();
 #if XORG_VERSION_CURRENT >= 11900000
     EPHYR_DBG("Remove notify FD for client sock %d",remoteVars.clientsock);
     RemoveNotifyFd(remoteVars.clientsock);
@@ -2403,6 +2566,218 @@ void readInputSelectionHeader(char* buff)
     pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
 }
 
+//length is only important for UDP connections. In case of TCP it'll be always EVLENGTH
+BOOL remote_process_client_event ( char* buff , int length)
+{
+    uint32_t event_type=*((uint32_t*)buff);
+    //updating keep alive time
+    pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+    remoteVars.last_client_keepalive_time=time(NULL);
+    if(remoteVars.client_version>=3 && remoteVars.checkKeepAliveTimer)
+    {
+        remoteVars.checkKeepAliveTimer=TimerSet(remoteVars.checkKeepAliveTimer,0,CLIENTALIVE_TIMEOUT, checkClientAlive, NULL);
+    }
+
+    pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+
+    switch(event_type)
+    {
+        case MotionNotify:
+        {
+            uint32_t x=*((uint32_t*)buff+1);
+            uint32_t y=*((uint32_t*)buff+2);
+
+            //                    EPHYR_DBG("HAVE MOTION EVENT %d, %d from client\n",x,y);
+            ephyrClientMouseMotion(x,y);
+            break;
+        }
+        case ButtonPress:
+        case ButtonRelease:
+        {
+            uint32_t state=*((uint32_t*)buff+1);
+            uint32_t button=*((uint32_t*)buff+2);
+            //                    EPHYR_DBG("HAVE BUTTON PRESS/RELEASE EVENT %d, %d from client\n",state,button);
+            ephyrClientButton(event_type,state, button);
+            break;
+        }
+        case KeyPress:
+        {
+            uint32_t state=*((uint32_t*)buff+1);
+            uint32_t key=*((uint32_t*)buff+2);
+            //                    EPHYR_DBG("HAVE KEY PRESS EVENT state: %d(%x), key: %d(%x) from client\n",state,state, key, key);
+            ephyrClientKey(event_type,state, key);
+            //send key release immeidately after key press to avoid "key sticking"
+            ephyrClientKey(KeyRelease,state, key);
+            break;
+        }
+        case KeyRelease:
+        {
+            uint32_t state=*((uint32_t*)buff+1);
+            uint32_t key=*((uint32_t*)buff+2);
+            //                    EPHYR_DBG("HAVE KEY RELEASE EVENT state: %d(%x), key: %d(%x) from client\n",state,state, key, key);
+            ephyrClientKey(event_type,state, key);
+            break;
+        }
+        case GEOMETRY:
+        {
+            uint16_t width=*((uint16_t*)buff+2);
+            uint16_t height=*((uint16_t*)buff+3);
+            struct VirtScreen screens[4] = {{0}};
+            remoteVars.client_initialized=TRUE;
+            EPHYR_DBG("Client want resize to %dx%d",width,height);
+            memset(screens,0, sizeof(struct VirtScreen)*4);
+            for(int j=0;j<4;++j)
+            {
+                char* record=buff+9+j*8;
+                screens[j].width=*((uint16_t*)record);
+                screens[j].height=*((uint16_t*)record+1);
+                screens[j].x=*((int16_t*)record+2);
+                screens[j].y=*((int16_t*)record+3);
+                if(!screens[j].width || !screens[j].height)
+                {
+                    break;
+                }
+                EPHYR_DBG("SCREEN %d - (%dx%d) - %d,%d", j, screens[j].width, screens[j].height, screens[j].x, screens[j].y);
+            }
+            ephyrResizeScreen (remoteVars.ephyrScreen->pScreen,width,height, screens);
+            break;
+        }
+        case UPDATE:
+        {
+            int32_t width=*((uint32_t*)buff+1);
+            int32_t height=*((uint32_t*)buff+2);
+            int32_t x=*((uint32_t*)buff+3);
+            int32_t y=*((uint32_t*)buff+4);
+            uint32_t winid=0;
+            if(remoteVars.rootless)
+            {
+                winid=*((uint32_t*)buff+5);
+            }
+            //                     EPHYR_DBG("HAVE UPDATE request from client, window 0x%X %dx%d %d,%d\n",winid, width, height, x,y );
+            pthread_mutex_lock(&remoteVars.mainimg_mutex);
+            //                     EPHYR_DBG("DBF: %p, %d, %d",remoteVars.main_img, remoteVars.main_img_width, remoteVars.main_img_height);
+            if(remoteVars.main_img  && x+width <= remoteVars.main_img_width  && y+height <= remoteVars.main_img_height )
+            {
+                pthread_mutex_unlock(&remoteVars.mainimg_mutex);
+                add_frame(width, height, x, y, 0, 0, winid);
+            }
+            else
+            {
+                EPHYR_DBG("UPDATE: skip request");
+                pthread_mutex_unlock(&remoteVars.mainimg_mutex);
+            }
+            break;
+        }
+        case SELECTIONEVENT:
+        {
+            readInputSelectionHeader(buff);
+            break;
+        }
+        case CLIENTVERSION:
+        {
+            int16_t ver=*((uint16_t*)buff+2);
+            int16_t os=*((uint16_t*)buff+3);
+            set_client_version(ver, os);
+            EPHYR_DBG("Client information: vesrion %d, os %d", ver, os);
+            pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+            break;
+        }
+        case DEMANDSELECTION:
+        {
+            int16_t sel=*((uint16_t*)buff+2);
+            //                     EPHYR_DBG("Client requesting selection %d", sel);
+            client_sel_request_notify(sel);
+            break;
+        }
+        case KEEPALIVE:
+        {
+            //receive keepalive event.  In UDP case we are also using it for synchronization
+            if(remoteVars.serverType==UDP)
+            {
+                int16_t lastContr=*((uint16_t*)buff+2);
+//                 EPHYR_DBG("Client synchronization, last control packet %d, last frame packet %d", lastContr, lastFr);
+                pthread_mutex_lock(&remoteVars.server_dgram_mutex);
+                remove_dgram_from_list(&(remoteVars.server_control_datagrams), lastContr, FALSE);
+                pthread_mutex_unlock(&remoteVars.server_dgram_mutex);
+            }
+            break;
+        }
+        case RESENDSCONTROL:
+        {
+            resend_dgrams(buff+4,length-4, ServerControlPacket);
+            break;
+        }
+        case RESENDFRAME:
+        {
+            resend_frame( *((uint32_t*)buff+1) );
+            break;
+        }
+        case CACHEREBUILD:
+        {
+            //rebuild all frame and cursors caches
+            rebuild_caches();
+            break;
+        }
+        case WINCHANGE:
+        {
+            client_win_change(buff);
+            break;
+        }
+        case DISCONNECTCLIENT:
+        {
+            EPHYR_DBG("Client sent disconnect event");
+            disconnect_client();
+            break;
+        }
+        default:
+        {
+            EPHYR_DBG("UNSUPPORTED EVENT: %d",event_type);
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+void
+resend_dgrams(char* buffer, int length, uint8_t dgType)
+{
+    int processed=0;
+    uint16_t packSeq;
+    uint16_t missedDgs;
+    uint16_t dgSeq;
+    uint16_t i;
+    while(processed<length)
+    {
+        packSeq=*( (uint16_t*) (buffer+processed) );
+        processed+=2;
+        missedDgs=*( (uint16_t*) (buffer+processed) );
+        processed+=2;
+        if(missedDgs)
+        {
+            EPHYR_DBG("Client missing %d datagrams, from packet %d, type %d",missedDgs, packSeq, dgType);
+            for(i=0;i<missedDgs;++i)
+            {
+                if(processed >= length)
+                {
+                    EPHYR_DBG("WARNING, Client requested wrong amount of datagrams, stop processing...");
+                    send_sync_failed_notification();
+                    return;
+                }
+                dgSeq=*( (uint16_t*) (buffer+processed) );
+                resend_dgram(dgType, packSeq, dgSeq);
+                processed+=2;
+            }
+            processed+=missedDgs*2;
+        }
+        else
+        {
+            EPHYR_DBG("Client missing packet %d, type %d", packSeq, dgType);
+            resend_dgram_packet(dgType, packSeq);
+        }
+    }
+}
+
+
 void
 clientReadNotify(int fd, int ready, void *data)
 {
@@ -2418,6 +2793,11 @@ clientReadNotify(int fd, int ready, void *data)
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     if(!con)
         return;
+    if(remoteVars.serverType==UDP)
+    {
+        remote_recv_dgram();
+        return;
+    }
 
     /* read max 99 events */
     length=read(remoteVars.clientsock,remoteVars.eventBuffer + remoteVars.evBufferOffset, EVLENGTH*99);
@@ -2425,6 +2805,7 @@ clientReadNotify(int fd, int ready, void *data)
     if(length<0)
     {
         EPHYR_DBG("WRONG data - %d\n",length);
+//         disconnect_client();
         return;
     }
     if(!length)
@@ -2435,7 +2816,6 @@ clientReadNotify(int fd, int ready, void *data)
     }
 //    EPHYR_DBG("Got ev bytes - %d\n",eventnum++);
 
-
     length+=remoteVars.evBufferOffset;
     iterations=length/EVLENGTH;
 
@@ -2449,219 +2829,11 @@ clientReadNotify(int fd, int ready, void *data)
         }
         else
         {
-            uint32_t event_type=*((uint32_t*)buff);
-
-            switch(event_type)
+            if(!remote_process_client_event(buff,0))
             {
-                case MotionNotify:
-                {
-                    uint32_t x=*((uint32_t*)buff+1);
-                    uint32_t y=*((uint32_t*)buff+2);
-
-//                    EPHYR_DBG("HAVE MOTION EVENT %d, %d from client\n",x,y);
-                    ephyrClientMouseMotion(x,y);
-                    break;
-                }
-                case ButtonPress:
-                case ButtonRelease:
-                {
-                    uint32_t state=*((uint32_t*)buff+1);
-                    uint32_t button=*((uint32_t*)buff+2);
-
-//                    EPHYR_DBG("HAVE BUTTON PRESS/RELEASE EVENT %d, %d from client\n",state,button);
-                    ephyrClientButton(event_type,state, button);
-                    break;
-                }
-                case KeyPress:
-                {
-                    uint32_t state=*((uint32_t*)buff+1);
-                    uint32_t key=*((uint32_t*)buff+2);
-
-//                    EPHYR_DBG("HAVE KEY PRESS EVENT state: %d(%x), key: %d(%x) from client\n",state,state, key, key);
-//                    if (state & ShiftMask)
-//                    {
-//                        EPHYR_DBG("SHIFT");
-//                    }
-//                    if (state & LockMask)
-//                    {
-//                        EPHYR_DBG("LOCK");
-//                    }
-//                    if (state & ControlMask)
-//                    {
-//                        EPHYR_DBG("CONTROL");
-//                    }
-//                    if (state & Mod1Mask)
-//                    {
-//                        EPHYR_DBG("MOD1");
-//                    }
-//                    if (state & Mod2Mask)
-//                    {
-//                        EPHYR_DBG("MOD2");
-//                    }
-//                    if (state & Mod3Mask)
-//                    {
-//                        EPHYR_DBG("MOD3");
-//                    }
-//                    if (state & Mod4Mask)
-//                    {
-//                        EPHYR_DBG("MOD4");
-//                    }
-//                    if (state & Mod5Mask)
-//                    {
-//                        EPHYR_DBG("MOD5");
-//                    }
-
-                    ephyrClientKey(event_type,state, key);
-
-//send key release immeidately after key press to avoid "key sticking"
-                    ephyrClientKey(KeyRelease,state, key);
-                    break;
-                }
-                case KeyRelease:
-                {
-                    uint32_t state=*((uint32_t*)buff+1);
-                    uint32_t key=*((uint32_t*)buff+2);
-
-//                    EPHYR_DBG("HAVE KEY RELEASE EVENT state: %d(%x), key: %d(%x) from client\n",state,state, key, key);
-//                    if (state & ShiftMask)
-//                    {
-//                        EPHYR_DBG("SHIFT");
-//                    }
-//                    if (state & LockMask)
-//                    {
-//                        EPHYR_DBG("LOCK");
-//                    }
-//                    if (state & ControlMask)
-//                    {
-//                        EPHYR_DBG("CONTROL");
-//                    }
-//                    if (state & Mod1Mask)
-//                    {
-//                        EPHYR_DBG("MOD1");
-//                    }
-//                    if (state & Mod2Mask)
-//                    {
-//                        EPHYR_DBG("MOD2");
-//                    }
-//                    if (state & Mod3Mask)
-//                    {
-//                        EPHYR_DBG("MOD3");
-//                    }
-//                    if (state & Mod4Mask)
-//                    {
-//                        EPHYR_DBG("MOD4");
-//                    }
-//                    if (state & Mod5Mask)
-//                    {
-//                        EPHYR_DBG("MOD5");
-//                    }
-                    ephyrClientKey(event_type,state, key);
-                    break;
-                }
-                case GEOMETRY:
-                {
-                    uint16_t width=*((uint16_t*)buff+2);
-                    uint16_t height=*((uint16_t*)buff+3);
-                    struct VirtScreen screens[4] = {{0}};
-
-                    remoteVars.client_initialized=TRUE;
-                    EPHYR_DBG("Client want resize to %dx%d",width,height);
-
-                    memset(screens,0, sizeof(struct VirtScreen)*4);
-                    for(int j=0;j<4;++j)
-                    {
-                        char* record=buff+9+j*8;
-                        screens[j].width=*((uint16_t*)record);
-                        screens[j].height=*((uint16_t*)record+1);
-                        screens[j].x=*((int16_t*)record+2);
-                        screens[j].y=*((int16_t*)record+3);
-
-                        if(!screens[j].width || !screens[j].height)
-                        {
-                            break;
-                        }
-                        EPHYR_DBG("SCREEN %d - (%dx%d) - %d,%d", j, screens[j].width, screens[j].height, screens[j].x, screens[j].y);
-                    }
-                    ephyrResizeScreen (remoteVars.ephyrScreen->pScreen,width,height, screens);
-                    break;
-                }
-                case UPDATE:
-                {
-                    int32_t width=*((uint32_t*)buff+1);
-                    int32_t height=*((uint32_t*)buff+2);
-
-                    int32_t x=*((uint32_t*)buff+3);
-                    int32_t y=*((uint32_t*)buff+4);
-                    uint32_t winid=0;
-                    if(remoteVars.rootless)
-                    {
-                        winid=*((uint32_t*)buff+5);
-                    }
-
-//                     EPHYR_DBG("HAVE UPDATE request from client, window 0x%X %dx%d %d,%d\n",winid, width, height, x,y );
-                    pthread_mutex_lock(&remoteVars.mainimg_mutex);
-
-//                     EPHYR_DBG("DBF: %p, %d, %d",remoteVars.main_img, remoteVars.main_img_width, remoteVars.main_img_height);
-
-                    if(remoteVars.main_img  && x+width <= remoteVars.main_img_width  && y+height <= remoteVars.main_img_height )
-                    {
-
-                        pthread_mutex_unlock(&remoteVars.mainimg_mutex);
-                        add_frame(width, height, x, y, 0, 0, winid);
-                    }
-                    else
-                    {
-                        EPHYR_DBG("UPDATE: skip request");
-
-                        pthread_mutex_unlock(&remoteVars.mainimg_mutex);
-                    }
-                    break;
-                }
-                case SELECTIONEVENT:
-                {
-                    readInputSelectionHeader(buff);
-                    break;
-                }
-                case CLIENTVERSION:
-                {
-                    int16_t ver=*((uint16_t*)buff+2);
-                    int16_t os=*((uint16_t*)buff+3);
-                    set_client_version(ver, os);
-                    EPHYR_DBG("Client information: vesrion %d, os %d", ver, os);
-                    pthread_cond_signal(&remoteVars.have_sendqueue_cond);
-                    break;
-                }
-                case DEMANDSELECTION:
-                {
-                    int16_t sel=*((uint16_t*)buff+2);
-//                     EPHYR_DBG("Client requesting selection %d", sel);
-                    client_sel_request_notify(sel);
-                    break;
-                }
-                case KEEPALIVE:
-                {
-                    //receive keepalive event, don't need to do anything
-                    break;
-                }
-                case CACHEREBUILD:
-                {
-                    //rebuild all frame and cursors caches
-                    rebuild_caches();
-                    break;
-                }
-                case WINCHANGE:
-                {
-                    client_win_change(buff);
-                    break;
-                }
-                default:
-                {
-                    EPHYR_DBG("UNSUPPORTED EVENT: %d",event_type);
-                    /* looks like we have some corrupted data, let's try to reset event buffer */
-                    remoteVars.evBufferOffset=0;
-                    length=0;
-                    break;
-                }
+                /* looks like we have some corrupted data, let's try to reset event buffer */
+                remoteVars.evBufferOffset=0;
+                length=0;
             }
         }
 //        EPHYR_DBG("Processed event - %d %d\n",eventnum++, eventbytes);
@@ -2924,7 +3096,14 @@ void set_client_version(uint16_t ver, uint16_t os)
     //Linux clients supporting sending selection on demand (not sending data if not needed)
     //Web client support clipboard and selection on demand starting from v.4
     remoteVars.selstruct.clientSupportsOnDemandSelection=(((ver > 1) && (os == OS_LINUX)) || ((ver > 3) && (os == WEB)));
-
+    if(remoteVars.client_version>=3)
+    {
+        //start timer for checking if client alive
+        pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+        remoteVars.checkKeepAliveTimer=TimerSet(0,0,CLIENTALIVE_TIMEOUT, checkClientAlive, NULL);
+        remoteVars.sendKeepAliveTimer=TimerSet(0,0,SERVERALIVE_TIMEOUT, sendServerAlive, NULL);
+        pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+    }
 }
 
 #if XORG_VERSION_CURRENT < 11900000
@@ -2992,64 +3171,86 @@ unsigned int checkSocketConnection(OsTimerPtr timer, CARD32 time, void* args)
 void serverAcceptNotify(int fd, int ready_sock, void *data)
 {
     int ret;
-    remoteVars.clientsock = accept ( remoteVars.serversock, (struct sockaddr *) &remoteVars.address, &remoteVars.addrlen );
-    if (remoteVars.clientsock <= 0)
-    {
-        EPHYR_DBG( "ACCEPT ERROR OR CANCELD!\n");
-        return;
-    }
-    EPHYR_DBG ("Connection from (%s)...\n", inet_ntoa (remoteVars.address.sin_addr));
+    char msg[33];
+    int length=32;
+    int ready=0;
 
-    //only accept one client, close server socket
-    close_server_socket();
-
-    if(strlen(remoteVars.acceptAddr))
+    if(remoteVars.serverType==TCP)
     {
-        struct addrinfo hints, *res;
-        int errcode;
-
-        memset (&hints, 0, sizeof (hints));
-        hints.ai_family = AF_INET;
-        hints.ai_socktype = SOCK_STREAM;
-        hints.ai_flags |= AI_CANONNAME;
-
-        errcode = getaddrinfo (remoteVars.acceptAddr, NULL, &hints, &res);
-        if (errcode != 0 || !res)
-        {
-            EPHYR_DBG ("ERROR LOOKUP %s", remoteVars.acceptAddr);
-            terminateServer(-1);
-        }
-        if(  ((struct sockaddr_in *) (res->ai_addr))->sin_addr.s_addr != remoteVars.address.sin_addr.s_addr)
+        remoteVars.clientsock = accept ( remoteVars.serversock, (struct sockaddr *) &remoteVars.address, &remoteVars.addrlen );
+        if (remoteVars.clientsock <= 0)
         {
-            EPHYR_DBG("Connection only allowed from %s",inet_ntoa ( ((struct sockaddr_in *)(res->ai_addr))->sin_addr));
-            shutdown(remoteVars.clientsock, SHUT_RDWR);
-            close(remoteVars.clientsock);
+            EPHYR_DBG( "ACCEPT ERROR OR CANCELD!\n");
             return;
         }
-    }
-    if(strlen(remoteVars.cookie))
-    {
-        char msg[33];
-        int length=32;
-        int ready=0;
+        EPHYR_DBG ("Connection from (%s)...\n", inet_ntoa (remoteVars.address.sin_addr));
 
-        //            EPHYR_DBG("Checking cookie: %s",remoteVars.cookie);
+        //only accept one client, close server socket
+        close_server_socket();
 
-        while(ready<length)
+        if(strlen(remoteVars.acceptAddr))
         {
-            int chunk=read(remoteVars.clientsock, msg+ready, 32-ready);
+            struct addrinfo hints, *res;
+            int errcode;
+
+            memset (&hints, 0, sizeof (hints));
+            hints.ai_family = AF_INET;
+            hints.ai_socktype = SOCK_STREAM;
+            hints.ai_flags |= AI_CANONNAME;
 
-            if(chunk<=0)
+            errcode = getaddrinfo (remoteVars.acceptAddr, NULL, &hints, &res);
+            if (errcode != 0 || !res)
+            {
+                EPHYR_DBG ("ERROR LOOKUP %s", remoteVars.acceptAddr);
+                terminateServer(-1);
+            }
+            if(  ((struct sockaddr_in *) (res->ai_addr))->sin_addr.s_addr != remoteVars.address.sin_addr.s_addr)
             {
-                EPHYR_DBG("READ COOKIE ERROR");
+                EPHYR_DBG("Connection only allowed from %s",inet_ntoa ( ((struct sockaddr_in *)(res->ai_addr))->sin_addr));
                 shutdown(remoteVars.clientsock, SHUT_RDWR);
                 close(remoteVars.clientsock);
                 return;
             }
-            ready+=chunk;
+        }
+        if(strlen(remoteVars.cookie))
+        {
+            while(ready<length)
+            {
+                int chunk=read(remoteVars.clientsock, msg+ready, 32-ready);
+                if(chunk<=0)
+                {
+                    EPHYR_DBG("READ COOKIE ERROR");
+                    shutdown(remoteVars.clientsock, SHUT_RDWR);
+                    close(remoteVars.clientsock);
+                    return;
+                }
+                ready+=chunk;
+            }
+        }
 
-            EPHYR_DBG("got %d COOKIE BYTES from client", ready);
+    }
+    else
+    {
+        ready = recvfrom(remoteVars.serversock, msg, length,  MSG_WAITALL,  (struct sockaddr *) &remoteVars.address, &remoteVars.addrlen);
+        EPHYR_DBG ("Connection from (%s)...\n", inet_ntoa (remoteVars.address.sin_addr));
+        remoteVars.clientsock=remoteVars.serversock;
+        ret=connect(remoteVars.clientsock, (struct sockaddr *) &remoteVars.address, remoteVars.addrlen);
+        if(ret)
+        {
+            EPHYR_DBG("Error, failed to connect to client socket: %s",gai_strerror(ret));
+            shutdown(remoteVars.clientsock, SHUT_RDWR);
+            close(remoteVars.clientsock);
+            return;
+        }
+        else
+        {
+            EPHYR_DBG("Connected to client UDP socket...");
         }
+    }
+
+    if(strlen(remoteVars.cookie))
+    {
+        EPHYR_DBG("got %d COOKIE BYTES from client", ready);
         if(strncmp(msg,remoteVars.cookie,32))
         {
             EPHYR_DBG("Wrong cookie");
@@ -3071,6 +3272,7 @@ void serverAcceptNotify(int fd, int ready_sock, void *data)
     #endif /* XORG_VERSION_CURRENT */
     remoteVars.client_connected=TRUE;
     remoteVars.server_version_sent=FALSE;
+    remoteVars.clientEventSeq=0-1;
     set_client_version(0,0);
     if(remoteVars.checkConnectionTimer)
     {
@@ -3112,9 +3314,19 @@ void open_socket(void)
 {
     const int y = 1;
 
-    remoteVars.serversock=socket (AF_INET, SOCK_STREAM, 0);
-    setsockopt( remoteVars.serversock, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
-    remoteVars.address.sin_family = AF_INET;
+    if(remoteVars.serverType==TCP)
+    {
+        EPHYR_DBG("Openning TCP socket...");
+        remoteVars.serversock=socket (AF_INET, SOCK_STREAM, 0);
+        setsockopt( remoteVars.serversock, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
+        remoteVars.address.sin_family = AF_INET;
+    }
+    else
+    {
+        EPHYR_DBG("Openning UDP socket...");
+        remoteVars.serversock=socket (AF_INET, SOCK_DGRAM, 0);
+        remoteVars.address.sin_family = AF_UNSPEC;
+    }
     remoteVars.address.sin_addr.s_addr = INADDR_ANY;
 
     if(! strlen(remoteVars.acceptAddr))
@@ -3146,7 +3358,7 @@ void open_socket(void)
 
 #if XORG_VERSION_CURRENT >= 11900000
     EPHYR_DBG("Set notify FD for server sock: %d",remoteVars.serversock);
-    EPHYR_DBG ("waiting for TCP connection\n");
+    EPHYR_DBG ("waiting for Client connection\n");
     SetNotifyFd(remoteVars.serversock, serverAcceptNotify, X_NOTIFY_READ, NULL);
 #endif /* XORG_VERSION_CURRENT */
 
@@ -3159,6 +3371,7 @@ void terminateServer(int exitStatus)
     setAgentState(TERMINATING);
     if(remoteVars.client_connected)
     {
+        send_srv_disconnect();
         disconnect_client();
         pthread_join(remoteVars.send_thread_id,NULL);
         remoteVars.send_thread_id=0;
@@ -3180,6 +3393,8 @@ void terminateServer(int exitStatus)
 
     pthread_mutex_destroy(&remoteVars.mainimg_mutex);
     pthread_mutex_destroy(&remoteVars.sendqueue_mutex);
+    pthread_mutex_destroy(&remoteVars.server_dgram_mutex);
+    pthread_mutex_destroy(&remoteVars.client_dgram_mutex);
     pthread_cond_destroy(&remoteVars.have_sendqueue_cond);
 
     if(remoteVars.main_img)
@@ -3346,7 +3561,11 @@ remote_init(void)
         remoteVars.initialJpegQuality=remoteVars.jpegQuality=JPG_QUALITY;
     EPHYR_DBG("JPEG quality is %d", remoteVars.initialJpegQuality);
     remoteVars.compression=DEFAULT_COMPRESSION;
-    remoteVars.selstruct.selectionMode = CLIP_BOTH;
+
+    remoteVars.selstruct.selectionMode = CLIP_NONE;
+#warning change this defaults values
+    remoteVars.serverType=UDP;
+
     if(!strlen(remote_get_init_geometry()))
     {
         EPHYR_DBG("Setting initial geometry to \"800x600\"");
@@ -3355,6 +3574,8 @@ remote_init(void)
 
     pthread_mutex_init(&remoteVars.mainimg_mutex, NULL);
     pthread_mutex_init(&remoteVars.sendqueue_mutex,NULL);
+    pthread_mutex_init(&remoteVars.server_dgram_mutex,NULL);
+    pthread_mutex_init(&remoteVars.client_dgram_mutex,NULL);
     pthread_cond_init(&remoteVars.have_sendqueue_cond,NULL);
 
     displayVar=secure_getenv("DISPLAY");
@@ -4434,6 +4655,89 @@ void remote_check_rootless_windows_for_updates(KdScreenInfo *screen)
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 }
 
+//check if the point belongs to region. If the point is close enough, but not inside of region, extend the region boundaries
+BOOL
+insideOfRegion(struct PaintRectRegion* reg, int x , int y)
+{
+    if(!reg)
+        return FALSE;
+    if(x >= reg->x1 - MAXDISTANCETOREGION && x <= reg->x2+MAXDISTANCETOREGION  &&  y >= reg->y1 - MAXDISTANCETOREGION && y<= reg->y2 + MAXDISTANCETOREGION)
+    {
+        if(x<reg->x1)
+            reg->x1=x;
+        if(x>reg->x2)
+            reg->x2=x;
+        if(y<reg->y1)
+            reg->y1=y;
+        if(y>reg->y2)
+            reg->y2=y;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+//find the region, point belongs to
+struct PaintRectRegion*
+findRegionForPoint(struct PaintRectRegion* firstRegion, int x , int y)
+{
+    while(firstRegion)
+    {
+        if(insideOfRegion(firstRegion, x,y))
+            return firstRegion;
+        firstRegion=firstRegion->next;
+    }
+    return firstRegion;
+}
+
+BOOL
+unitePaintRegions(struct PaintRectRegion* firstRegion)
+{
+    struct PaintRectRegion* next;
+    int ux1,ux2,uy1,uy2;
+
+    BOOL haveUnited=FALSE;
+    if(!firstRegion)
+        return FALSE;
+    if(!firstRegion->next)
+        return FALSE;
+    next=firstRegion->next;
+    while(next)
+    {
+        if(!next->united)
+        {
+           //check if we need to unite with this one
+            ux1=(firstRegion->x1 < next->x1)?firstRegion->x1:next->x1;
+            ux2=(firstRegion->x2 > next->x2)?firstRegion->x2:next->x2;
+            uy1=(firstRegion->y1 < next->y1)?firstRegion->y1:next->y1;
+            uy2=(firstRegion->y2 > next->y2)?firstRegion->y2:next->y2;
+            //if united size is smaller or same as separated size, we'll unite them
+            if(
+                ((firstRegion->x2-firstRegion->x1 +1)*(firstRegion->y2-firstRegion->y1+1) + (next->x2-next->x1 +1)*(next->y2-next->y1+1))>
+                (ux2-ux1 +1)*(uy2-uy1+1) )
+            {
+//                 EPHYR_DBG("Uniting regions %d:%d-%d:%d and %d:%d-%d:%d", firstRegion->x1, firstRegion->y1, firstRegion->x2, firstRegion->y2, next->x1, next->y1, next->x2, next->y2);
+                haveUnited=TRUE;
+                next->united=TRUE;
+                firstRegion->x1=ux1;
+                firstRegion->x2=ux2;
+                firstRegion->y1=uy1;
+                firstRegion->y2=uy2;
+            }
+        }
+        next=next->next;
+    }
+    next=firstRegion->next;
+    while(next)
+    {
+        if(!next->united)
+        {
+           return haveUnited||unitePaintRegions(next);
+        }
+        next=next->next;
+    }
+    return haveUnited;
+}
+
 void
 remote_paint_rect(KdScreenInfo *screen,
                   int sx, int sy, int dx, int dy, int width, int height)
@@ -4443,6 +4747,17 @@ remote_paint_rect(KdScreenInfo *screen,
 
 
     uint32_t size=width*height*XSERVERBPP;
+    struct PaintRectRegion *regions=NULL;
+    struct PaintRectRegion *currentRegion=NULL;
+    struct PaintRectRegion *lastRegion=NULL;
+    uint32_t reg_size;
+    int reg_width, reg_height;
+    int numOfRegs=0;
+    int totalSizeOfRegions=0;
+    int totalDirtySize=0;
+    BOOL allUnited=FALSE;
+
+
     if(remoteVars.rootless)
     {
         remote_check_rootless_windows_for_updates(screen);
@@ -4458,7 +4773,7 @@ remote_paint_rect(KdScreenInfo *screen,
         char maxdiff = 0;
         char mindiff = 0;
 
-//        EPHYR_DBG("REPAINT %dx%d sx %d, sy %d, dx %d, dy %d", width, height, sx, sy, dx, dy);
+//         EPHYR_DBG("---REPAINT %d:%d,  %dx%d", dx, dy, width, height);
 
         dirtyx_max=dx-1;
         dirtyy_max=dy-1;
@@ -4514,6 +4829,25 @@ remote_paint_rect(KdScreenInfo *screen,
                 }
                 if(pixIsDirty)
                 {
+                    if(!insideOfRegion(currentRegion,x,y))
+                    {
+                        //point is not inside of current region, find the region, point belongs to
+                        currentRegion=findRegionForPoint(regions, x, y);
+                        if(!currentRegion)
+                        {
+                            //creating new region and add it to the end of the list
+                            currentRegion=malloc(sizeof(struct PaintRectRegion));
+                            currentRegion->next=NULL;
+                            currentRegion->united=FALSE;
+                            currentRegion->x1=currentRegion->x2=x;
+                            currentRegion->y1=currentRegion->y2=y;
+                            if(lastRegion)
+                                lastRegion->next=currentRegion;
+                            if(!regions)
+                                regions=currentRegion;
+                            lastRegion=currentRegion;
+                        }
+                    }
                     if(x>dirtyx_max)
                     {
                         dirtyx_max=x;
@@ -4539,28 +4873,102 @@ remote_paint_rect(KdScreenInfo *screen,
 
         pthread_mutex_unlock(&remoteVars.mainimg_mutex);
 
-
-//        EPHYR_DBG("DIRTY %d,%d - %d,%d", dirtyx_min, dirtyy_min, dirtyx_max, dirtyy_max);
-
-
         width=dirtyx_max-dirtyx_min+1;
         height=dirtyy_max-dirtyy_min+1;
+//         EPHYR_DBG("DIRTY %d:%d,  %dx%d---", dirtyx_min, dirtyy_min, width, height);
 //        int oldsize=size;
-        size=width*height*XSERVERBPP;
+        totalDirtySize=size=width*height*XSERVERBPP;
         if(width<=0 || height<=0||size<=0)
         {
-//            EPHYR_DBG("NO CHANGES DETECTED, NOT UPDATING");
+//             EPHYR_DBG("NO CHANGES DETECTED, NOT UPDATING");
             return;
         }
         dx=sx=dirtyx_min;
         dy=sy=dirtyy_min;
+//         remoteVars.sizeOfRects+=totalDirtySize;
 
 //        if(size!=oldsize)
 //        {
 //            EPHYR_DBG("new update rect dimensions: %dx%d", width, height);
 //        }
+/*
+#warning debug block
+        currentRegion=regions;
+        while(currentRegion)
+        {
+            totalSizeOfRegions+=(currentRegion->x2-currentRegion->x1 +1)*(currentRegion->y2-currentRegion->y1+1)*XSERVERBPP;
+            numOfRegs++;
+            currentRegion=currentRegion->next;
+        }
+//         EPHYR_DBG("---before union, regions: %d, total size %d", numOfRegs, totalSizeOfRegions);
+        totalSizeOfRegions=0;
+        numOfRegs=0;
+
+//end of debug block
+*/
+        while(!allUnited)
+        {
+            currentRegion=regions;
+            allUnited=TRUE;
+            while(currentRegion)
+            {
+                if(!currentRegion->united)
+                {
+                    if(unitePaintRegions(currentRegion))
+                    {
+                        allUnited=FALSE;
+                    }
+                }
+                currentRegion=currentRegion->next;
+            }
+        }
+        currentRegion=regions;
+        while(currentRegion)
+        {
+            if(!currentRegion->united)
+            {
+                totalSizeOfRegions+=(currentRegion->x2-currentRegion->x1 +1)*(currentRegion->y2-currentRegion->y1+1)*XSERVERBPP;
+            }
+            currentRegion=currentRegion->next;
+        }
+        currentRegion=regions;
+        while(currentRegion)
+        {
+            if(!currentRegion->united)
+            {
+                reg_width=currentRegion->x2-currentRegion->x1 +1;
+                reg_height=currentRegion->y2-currentRegion->y1+1;
+                reg_size=reg_width*reg_height*XSERVERBPP;
+                numOfRegs++;
+                if(totalDirtySize-totalSizeOfRegions > 0)
+                {
+                    add_frame(reg_width, reg_height, currentRegion->x1, currentRegion->y1, calculate_crc(reg_width, reg_height, currentRegion->x1,currentRegion->y1), reg_size,0);
+                }
+                //             EPHYR_DBG("Region %d - %d:%d %dx%d", numOfRegs, currentRegion->x1, currentRegion->y1, reg_width, reg_height);
+            }
+            regions=currentRegion;
+            currentRegion=currentRegion->next;
+            free(regions);
+        }
+
+//         EPHYR_DBG("---after union, regions: %d, total size %d", numOfRegs, totalSizeOfRegions);
+        /*if(totalDirtySize-totalSizeOfRegions > 0)
+        {
+            remoteVars.sizeOfRegions+=totalSizeOfRegions;
+            EPHYR_DBG("rects %lu, regs %lu, transferred %d%%", remoteVars.sizeOfRects, remoteVars.sizeOfRegions,
+                      (int)((((double)remoteVars.sizeOfRegions) / ((double)remoteVars.sizeOfRects))*100)) ;
+            //EPHYR_DBG("regions: %d dirty size=%d, total reg size=%d, diff=%d ",numOfRegs, totalDirtySize, totalSizeOfRegions, totalDirtySize-totalSizeOfRegions);
+        }*/
 
-        add_frame(width, height, dx, dy, calculate_crc(width, height,dx,dy), size,0);
+        if(totalDirtySize-totalSizeOfRegions <= 0)
+        {
+            /*if(totalDirtySize-totalSizeOfRegions < 0)
+            {
+                EPHYR_DBG("TOTAL SIZE of regions worse than dirty rect %d %d",totalDirtySize, totalSizeOfRegions);
+            }
+            remoteVars.sizeOfRegions+=totalDirtySize;*/
+            add_frame(width, height, dx, dy, calculate_crc(width, height,dx,dy), size,0);
+        }
     }
 }
 
@@ -4723,10 +5131,6 @@ void rebuild_caches(void)
 {
     EPHYR_DBG("CLIENT REQUESTED CLEARING ALL CACHES AND QUEUES");
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
-    clear_send_queue();
-    clear_frame_cache(0);
-    freeCursors();
-    delete_all_windows();
     remoteVars.cache_rebuilt=TRUE;
     pthread_cond_signal(&remoteVars.have_sendqueue_cond);
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
@@ -4804,3 +5208,627 @@ void send_dirty_region(int index)
     remoteVars.compression=compression;
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
 }
+
+//split packet to datagrams and send it
+int send_packet_as_datagrams(unsigned char* data, uint32_t length, uint8_t dgType)
+{
+    /*split to datagrams, reserve header space for each dgram, set sequence number for each dgram ,set correct flag FOR EACH DGRAM and send all datagrams*/
+
+    uint16_t* seqNumber;
+    uint16_t dgSeqNumber=0;
+    uint16_t dgInPack;
+    uint32_t sent_bytes=0;
+    uint16_t dgram_length;
+    unsigned char* dgram;
+    uint32_t checksum;
+    int writeRes;
+    uint16_t syncPackSeq=0;
+    pthread_mutex_lock(&remoteVars.server_dgram_mutex);
+
+    dgInPack=length/(UDPDGRAMSIZE-SRVDGRAMHEADERSIZE);
+    if(length%(UDPDGRAMSIZE-SRVDGRAMHEADERSIZE))
+        ++dgInPack;
+
+//     EPHYR_DBG("Sending DG packet of %d bytes",length);
+    //setting sequence number
+    switch(dgType)
+    {
+        case ServerControlPacket:
+            seqNumber=&remoteVars.controlPacketSeq;
+            break;
+        case ServerFramePacket:
+            seqNumber=&remoteVars.framePacketSeq;
+            break;
+        case ServerRepaintPacket:
+            seqNumber=&remoteVars.repaintPacketSeq;
+            break;
+        default:
+            //always 0 for sync packets
+            seqNumber=&syncPackSeq;
+            break;
+    }
+    //         EPHYR_DBG("Seq number: %d", *seqNumber);
+    while(sent_bytes<length)
+    {
+        if((length-sent_bytes)+SRVDGRAMHEADERSIZE <= UDPDGRAMSIZE)
+        {
+            dgram_length=(length-sent_bytes)+SRVDGRAMHEADERSIZE;
+        }
+        else
+        {
+            dgram_length=UDPDGRAMSIZE;
+        }
+
+        dgram=malloc(dgram_length);
+        memset(dgram,0,SRVDGRAMHEADERSIZE);
+        *((uint16_t*)dgram+2)=(*seqNumber);
+        *((uint16_t*)dgram+3)=dgInPack;
+        *((uint16_t*)dgram+4)=dgSeqNumber++;
+        *((uint8_t*)dgram+10)=dgType;
+        memcpy(dgram+SRVDGRAMHEADERSIZE, data+sent_bytes, dgram_length-SRVDGRAMHEADERSIZE);
+
+        checksum=crc32(0L, Z_NULL, 0);
+        checksum=crc32(checksum,dgram,dgram_length);
+        //setting checksum
+        *((uint32_t*)dgram)=checksum;
+        writeRes=write(remoteVars.clientsock, dgram, dgram_length);
+        if(writeRes!=(int)dgram_length)
+        {
+//             EPHYR_DBG("Warning, sending packet failed. Sent %d bytes instead of %d",writeRes,dgram_length);
+        }
+        sent_bytes+=(dgram_length-SRVDGRAMHEADERSIZE);
+        switch(dgType)
+        {
+            case ServerControlPacket:
+            {
+                //add dgram to list
+                add_dgram_to_list(dgram, dgram_length, &remoteVars.server_control_datagrams);
+                break;
+            }
+            default:
+                free(dgram);
+        }
+    }
+    (*seqNumber)++;
+    pthread_mutex_unlock(&remoteVars.server_dgram_mutex);
+    return sent_bytes;
+}
+//add dgram to list
+void
+add_dgram_to_list(unsigned char*  dgram, uint16_t length, struct dgram_element** dg_list)
+{
+    //dgram mutex is locked on this point
+    struct dgram_element* dg=malloc(sizeof(struct dgram_element));
+    struct dgram_element* current;
+    dg->length=length;
+    dg->data=dgram;
+    dg->next=NULL;
+    if(*dg_list)
+    {
+        current=*dg_list;
+        while(current->next)
+        {
+            current=current->next;
+        }
+        current->next=dg;
+    }
+    else
+    {
+        *dg_list=dg;
+    }
+}
+
+//remove datagrams from list
+// if !remove_all, stop after removing packet with seq==last_pack_seq
+void
+remove_dgram_from_list(struct dgram_element** dg_list, uint16_t last_pack_seq, BOOL remove_all)
+{
+
+    //dgram mutex should be locked on this point!!!
+    struct dgram_element *current, *next, *prev=NULL;
+    uint16_t current_seq;
+    int32_t current_seq_long;
+    int32_t last_pack_seq_long;
+
+    current=*dg_list;
+    while(current)
+    {
+        current_seq= *((uint16_t*)(current->data)+2);
+        current_seq_long=current_seq;
+        last_pack_seq_long=last_pack_seq;
+        //this is for the case when the seq is going over the max of the uint16
+        if(abs(current_seq_long-last_pack_seq_long)>64535 && (current_seq_long<1000) )
+        {
+            current_seq_long+=65536;
+        }
+        if(abs(current_seq_long-last_pack_seq_long)>64535 && (last_pack_seq_long<1000) )
+        {
+            last_pack_seq_long+=65536;
+        }
+
+        if(remove_all || (current_seq_long <= last_pack_seq_long))
+        {
+            next=current->next;
+            if(prev)
+                prev->next=next;
+            if(*dg_list==current)
+                *dg_list=next;
+            free(current->data);
+            free(current);
+            current=next;
+        }
+        else
+        {
+            prev=current;
+            current=current->next;
+        }
+    }
+}
+
+void
+resend_dgram(uint8_t dgType, uint16_t packSeq, uint16_t dgSeq)
+{
+    uint16_t dgram_length;
+    unsigned char* dgram=NULL;
+    int write_res;
+    struct dgram_element *list, *cur;
+    switch (dgType)
+    {
+        case ServerControlPacket:
+            list=remoteVars.server_control_datagrams;
+            break;
+        default:
+            EPHYR_DBG("Requested resending dgram of unsupported type %d", dgType);
+            send_sync_failed_notification();
+            return;
+    }
+    pthread_mutex_lock(&remoteVars.server_dgram_mutex);
+    cur=list;
+    while(cur)
+    {
+        if((*((uint16_t*)cur->data+2)==packSeq) && (*((uint16_t*)cur->data+4)==dgSeq))
+        {
+            break;
+        }
+        cur=cur->next;
+    }
+    if(!cur)
+    {
+        EPHYR_DBG("WARNING! requested datagram of type %d with packet sequense %d and dg sequence %d not found in list", dgType, packSeq, dgSeq);
+    }
+    else
+    {
+        dgram_length=cur->length;
+        dgram=malloc(cur->length);
+        memcpy(dgram, cur->data, dgram_length);
+    }
+    pthread_mutex_unlock(&remoteVars.server_dgram_mutex);
+    if(!dgram)
+    {
+        send_sync_failed_notification();
+        return;
+    }
+    write_res=write(remoteVars.clientsock, dgram, dgram_length);
+    if(write_res!=(int)dgram_length)
+    {
+        EPHYR_DBG("Warning, sending packet failed. Sent %d bytes instead of %d",write_res,dgram_length);
+    }
+    free(dgram);
+}
+
+void
+resend_dgram_packet(uint8_t dgType, uint16_t packSeq)
+{
+    uint16_t dg_in_pack=0;
+    uint16_t i;
+    struct dgram_element *list, *cur;
+    switch (dgType)
+    {
+        case ServerControlPacket:
+            list=remoteVars.server_control_datagrams;
+            break;
+        default:
+            EPHYR_DBG("Requested resending dgram of unsupported type %d", dgType);
+            send_sync_failed_notification();
+            return;
+    }
+    pthread_mutex_lock(&remoteVars.server_dgram_mutex);
+    cur=list;
+    while(cur)
+    {
+        if(*((uint16_t*)cur->data+2)==packSeq)
+        {
+            break;
+        }
+        cur=cur->next;
+    }
+    if(!cur)
+    {
+        EPHYR_DBG("WARNING! requested packet of type %d with packet sequense %d not found in list", dgType, packSeq);
+        pthread_mutex_unlock(&remoteVars.server_dgram_mutex);
+        send_sync_failed_notification();
+        return;
+    }
+    else
+    {
+        dg_in_pack=*((uint16_t*)cur->data+3);
+    }
+    pthread_mutex_unlock(&remoteVars.server_dgram_mutex);
+    EPHYR_DBG("resending packet of type %d with sequence %d and number of packets %d", dgType, packSeq, dg_in_pack);
+    for(i=0;i<dg_in_pack;++i)
+        resend_dgram(dgType, packSeq, i);
+}
+
+unsigned int
+checkClientAlive(OsTimerPtr timer, CARD32 time_card, void* args)
+{
+    time_t time_diff;
+    pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+    time_diff=time(NULL)-remoteVars.last_client_keepalive_time;
+    pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+
+    if(time_diff>=CLIENTALIVE_TIMEOUT/1000)
+    {
+        EPHYR_DBG("no data from client since %d seconds, diconnecting...", (int)time_diff);
+        disconnect_client();
+        return 0;
+    }
+    return CLIENTALIVE_TIMEOUT;
+}
+
+unsigned int sendServerAlive(OsTimerPtr timer, CARD32 time_card, void* args)
+{
+    unsigned char buffer[56] = {0};
+    _X_UNUSED int l;
+
+    *((uint32_t*)buffer)=SRVKEEPALIVE; //4B
+    if(remoteVars.serverType==TCP)
+    {
+        l=write(remoteVars.clientsock,buffer,56);
+    }
+    else
+    {
+        //send also synchronization data
+        pthread_mutex_lock(&remoteVars.client_dgram_mutex);
+        *((uint16_t*)buffer+2)=remoteVars.clientEventSeq;
+        pthread_mutex_unlock(&remoteVars.client_dgram_mutex);
+        l=send_packet_as_datagrams(buffer, 6, ServerSyncPacket);
+    }
+    return SERVERALIVE_TIMEOUT;
+}
+
+void
+send_srv_disconnect(void)
+{
+        unsigned char buffer[56] = {0};
+    _X_UNUSED int l;
+
+    if(remoteVars.client_version<3)
+        return;
+    *((uint32_t*)buffer)=SRVDISCONNECT; //4B
+    if(remoteVars.serverType==TCP)
+    {
+        l=write(remoteVars.clientsock,buffer,56);
+    }
+    else
+    {
+        l=send_packet_as_datagrams(buffer, 4, ServerSyncPacket);
+    }
+}
+
+void
+send_sync_failed_notification(void)
+{
+    unsigned char buffer[56] = {0};
+    //only using with UDP connections
+    if(remoteVars.serverType==TCP)
+    {
+        return;
+    }
+
+    *((uint32_t*)buffer)=SRVSYNCFAILED; //4B
+    send_packet_as_datagrams(buffer, 4, ServerControlPacket);
+}
+
+void
+clean_everything(void)
+{
+    //sendqueue_mutex is locked
+    clear_send_queue();
+    clear_frame_cache(0);
+    freeCursors();
+    clear_output_selection();
+    delete_all_windows();
+    pthread_mutex_lock(&remoteVars.server_dgram_mutex);
+    remove_dgram_from_list(&remoteVars.server_control_datagrams, 0, TRUE);
+    remoteVars.framePacketSeq=remoteVars.controlPacketSeq=remoteVars.repaintPacketSeq=0;
+    pthread_mutex_unlock(&remoteVars.server_dgram_mutex);
+    pthread_mutex_lock(&remoteVars.client_dgram_mutex);
+    remove_dgram_from_list(&remoteVars.client_event_datagrams, 0, TRUE);
+    remove_resend_request(0, TRUE);
+    remoteVars.clientEventSeq=0-1;
+    pthread_mutex_unlock(&remoteVars.client_dgram_mutex);
+}
+
+struct dgram_element* find_dgram_in_list(struct dgram_element** dg_list, uint16_t pack_seq)
+{
+    //dgram mutex should be locked on this point!!!
+    struct dgram_element *current;
+    uint16_t current_seq;
+    current=*dg_list;
+    while(current)
+    {
+        current_seq= *((uint16_t*)(current->data)+2);
+        if(current_seq==pack_seq)
+            break;
+        current=current->next;
+    }
+    return current;
+}
+
+void
+remote_check_event_packet_integrity(uint16_t seq_to_process)
+{
+    struct dgram_element* dgram;
+    unsigned char* dgram_data;
+    uint16_t current_seq;
+    uint16_t dgram_length;
+    struct timeval tv;
+    time_t curmsec;
+    BOOL have_missing=FALSE;
+    uint16_t *packets_process, *packets_missing;
+    int process_len=0, missing_len=0, i;
+    int max_len;
+    struct dgram_request_element *req;
+    unsigned char buffer[6];//4B EVENT and 2B missing seq;
+
+    gettimeofday(&tv, NULL);
+    curmsec=tv.tv_sec*1000+tv.tv_usec/1000;
+
+    pthread_mutex_lock(&remoteVars.client_dgram_mutex);
+    current_seq=remoteVars.clientEventSeq+1;
+    //went over max of uint16_t
+    if(current_seq>seq_to_process)
+        max_len=((int)seq_to_process+65536)-current_seq+1;
+    else
+        max_len=seq_to_process-current_seq+1;
+    packets_process=malloc(sizeof(uint16_t)*max_len);
+    packets_missing=malloc(sizeof(uint16_t)*max_len);
+
+    while(1)
+    {
+        dgram=find_dgram_in_list(&remoteVars.client_event_datagrams, current_seq);
+        if(!dgram)
+        {
+            have_missing=TRUE;
+            req=find_resend_request(current_seq);
+            if(!req)
+            {
+                //create new request
+                req=malloc(sizeof(struct dgram_request_element));
+                req->next=NULL;
+                req->seq=current_seq;
+                req->msec=curmsec;
+                if(remoteVars.cl_event_resend_request_last)
+                {
+                    remoteVars.cl_event_resend_request_last->next=req;
+                }
+                remoteVars.cl_event_resend_request_last=req;
+                if(!remoteVars.cl_event_resend_request_first)
+                {
+                    remoteVars.cl_event_resend_request_first=req;
+                }
+                packets_missing[missing_len++]=current_seq;
+                EPHYR_DBG("Missing event datagram %d",current_seq);
+            }
+            else
+            {
+                //already requested this dgram;
+                if(curmsec > req->msec + 200)
+                {//200 msec ellapsed since we requested it, requesting again
+                    packets_missing[missing_len++]=current_seq;
+                    req->msec=curmsec;
+                    EPHYR_DBG("Still missing event datagram %d, requesting again",current_seq);
+                }
+            }
+        }
+        else
+        {
+            if(!have_missing)
+            {
+                //till now all packets are ok
+                remoteVars.clientEventSeq=packets_process[process_len++]=current_seq;
+            }
+        }
+        if(current_seq==seq_to_process)
+            break;
+        ++current_seq;
+    }
+    pthread_mutex_unlock(&remoteVars.client_dgram_mutex);
+
+    //process events
+    for(i=0;i<process_len;++i)
+    {
+        pthread_mutex_lock(&remoteVars.client_dgram_mutex);
+        dgram_data=NULL;
+
+        dgram=find_dgram_in_list(&remoteVars.client_event_datagrams, packets_process[i]);
+        if(dgram)
+        {
+            dgram_data=malloc(dgram->length);
+            dgram_length=dgram->length;
+            memcpy(dgram_data, dgram->data, dgram->length);
+        }
+        remove_dgram_from_list(&remoteVars.client_event_datagrams, packets_process[i], FALSE);
+        pthread_mutex_unlock(&remoteVars.client_dgram_mutex);
+        if(dgram_data)
+        {
+            remote_process_client_event((char*)dgram_data+CLDGRAMHEADERSIZE, dgram_length-CLDGRAMHEADERSIZE);
+            free(dgram_data);
+        }
+    }
+
+    *((uint32_t*)buffer)=RESENDEVENTS; //4B
+    //request resend missing events
+    for(i=0;i<missing_len;++i)
+    {
+        *((uint16_t*)buffer+2)=packets_missing[i];
+        send_packet_as_datagrams(buffer, 6, ServerSyncPacket);
+    }
+    free(packets_missing);
+    free(packets_process);
+}
+
+void
+remote_recv_dgram(void)
+{
+    char buffer[UDPDGRAMSIZE];
+    uint32_t crc, checksum;
+    uint16_t seq;
+    uint8_t dgType;
+    int32_t current_long;
+    int32_t last_long;
+    unsigned char* dgram;
+
+
+    int length=read(remoteVars.clientsock,buffer, UDPDGRAMSIZE);
+//     EPHYR_DBG("Read datagram with size %d",length);
+    if(length<=CLDGRAMHEADERSIZE)
+    {
+//         EPHYR_DBG("Error reading datagram, the size is smaller than a header size: %d", length);
+        return;
+    }
+    checksum=*((uint32_t*)buffer);
+    memset(buffer, 0, 4);
+    crc=crc32(0L, Z_NULL, 0);
+    crc=crc32(crc,(unsigned char*)buffer,length);
+    if(crc!=checksum)
+    {
+        EPHYR_DBG("checksum failed %d instead of %d",crc,checksum);
+        return;
+    }
+    seq=*((uint16_t*)buffer+2);
+    dgType=*((uint8_t*)buffer+6);
+
+    switch(dgType)
+    {
+        case ClientEventPacket:
+//             EPHYR_DBG("Client event DG %d",seq);
+            break;
+        default:
+            //Sync packets should be procesed immeidately
+            remote_process_client_event(buffer+CLDGRAMHEADERSIZE, length-CLDGRAMHEADERSIZE);
+            return;
+    }
+    current_long=seq;
+    pthread_mutex_lock(&remoteVars.client_dgram_mutex);
+    last_long=remoteVars.clientEventSeq;
+    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)
+    {
+        EPHYR_DBG("Late client event sequence arrived: %d, last processed: %d", seq, remoteVars.clientEventSeq);
+        pthread_mutex_unlock(&remoteVars.client_dgram_mutex);
+        return;
+    }
+    dgram=malloc(length);
+    memcpy(dgram,buffer,length);
+    add_dgram_to_list(dgram, length, &remoteVars.client_event_datagrams);
+    //if it's resendet dgram, remove request
+    remove_resend_request(seq, FALSE);
+    pthread_mutex_unlock(&remoteVars.client_dgram_mutex);
+    remote_check_event_packet_integrity(seq);
+}
+
+struct dgram_request_element*
+find_resend_request(uint16_t seq)
+{
+    struct dgram_request_element* cur=remoteVars.cl_event_resend_request_first;
+    while(cur)
+    {
+        if(cur->seq==seq)
+            return cur;
+        cur=cur->next;
+    }
+    return NULL;
+}
+
+void
+remove_resend_request(uint16_t seq, BOOL all)
+{
+    struct dgram_request_element* cur=remoteVars.cl_event_resend_request_first;
+    struct dgram_request_element *next, *prev=NULL;
+    while(cur)
+    {
+        next=cur->next;
+        if(all)
+        {
+            free(cur);
+        }
+        else
+        {
+            if(cur->seq==seq)
+            {
+                if(prev)
+                {
+                    prev->next=next;
+                }
+                if(cur==remoteVars.cl_event_resend_request_first)
+                {
+                    remoteVars.cl_event_resend_request_first=next;
+                }
+                if(cur==remoteVars.cl_event_resend_request_last)
+                {
+                    remoteVars.cl_event_resend_request_last=prev;
+                }
+                free(cur);
+            }
+            else
+            {
+                prev=cur;
+            }
+
+        }
+        cur=next;
+    }
+    if(all)
+    {
+        remoteVars.cl_event_resend_request_first=remoteVars.cl_event_resend_request_last=NULL;
+    }
+}
+
+void
+resend_frame(uint32_t crc)
+{
+
+    unsigned char* data;
+    unsigned char* packet;
+    uint32_t size;
+    struct cache_elem* frame=find_cache_element(crc);
+    EPHYR_DBG("Client asks to resend frame from cash with crc %x",crc);
+    if(remoteVars.serverType==TCP)
+        return;
+    pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+    if(! frame)
+    {
+        EPHYR_DBG("requested frame not found in cache");
+        pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+        return;
+    }
+    data=image_compress(frame->width, frame->height, frame->data, &(size), CACHEBPP, 0l);
+    pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+    packet=malloc(size+8);
+    *((uint32_t*)packet)=CACHEFRAME;
+    *((uint32_t*)packet+1)=crc;
+    memcpy(packet+8, data, size);
+    free(data);
+    send_packet_as_datagrams(packet,size+8,ServerFramePacket);
+}
diff --git a/x2gokdriveremote.h b/x2gokdriveremote.h
index 68675f8..aed4902 100644
--- a/x2gokdriveremote.h
+++ b/x2gokdriveremote.h
@@ -100,15 +100,26 @@
 //Changes 4 - 5: support for CACHEREBUILD event
 //Changes 5 - 6: support for rootless mode
 //Changes 6 - 7: Sending KEYRELEASE immediately after KEYPRESS to avoid the "key sticking"
+//Changes 7 - 8: support for UDP sockets
 
-#define FEATURE_VERSION 7
+#define FEATURE_VERSION 8
 
 #define MAXMSGSIZE 1024*16
 
+//max size for UDP dgram 1200 (experimental value for VPN, maybe we should determine the MTU size later)
+#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)
+
 //port to listen by default
 #define DEFAULT_PORT 15000
 
 #define ACCEPT_TIMEOUT 30000 //msec
+#define CLIENTALIVE_TIMEOUT 30000 //msec
+#define SERVERALIVE_TIMEOUT 5000 //msec
 
 //if true, will save compressed jpg in file
 #define JPGDEBUG FALSE
@@ -121,7 +132,7 @@
 //always 4
 #define XSERVERBPP 4
 
-enum msg_type{FRAME,DELETED, CURSOR, DELETEDCURSOR, SELECTION, SERVERVERSION, DEMANDCLIENTSELECTION, REINIT, WINUPDATE};
+enum msg_type{FRAME,DELETED, CURSOR, DELETEDCURSOR, SELECTION, SERVERVERSION, DEMANDCLIENTSELECTION, REINIT, WINUPDATE, SRVKEEPALIVE, SRVDISCONNECT, SRVSYNCFAILED, RESENDEVENTS, CACHEFRAME};
 enum AgentState{STARTING, RUNNING, RESUMING, SUSPENDING, SUSPENDED, TERMINATING, TERMINATED};
 enum Compressions{JPEG,PNG};
 enum SelectionType{PRIMARY,CLIPBOARD};
@@ -136,6 +147,22 @@ enum WinType{WINDOW_TYPE_DESKTOP, WINDOW_TYPE_DOCK, WINDOW_TYPE_TOOLBAR, WINDOW_
 //new state requested by WINCHANGE event
 enum WinState{WIN_UNCHANGED, WIN_DELETED, WIN_ICONIFIED};
 
+//which type of server socket to use
+enum ServerType{TCP, UDP};
+
+//UDP datagrams types
+enum ServerDgramTypes{
+    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
+};
+
+enum ClientDgramType{
+    ClientEventPacket, //Event packet, high priority
+    ClientSyncPacket //Packet with synchronization data
+};
+
 //Size of 1 window update (new or changed window) = 4xwinId + type of update + 7 coordinates + visibility + type of window + size of name buffer + icon_size
 #define WINUPDSIZE 4*sizeof(uint32_t) + sizeof(int8_t) + 7*sizeof(int16_t) + sizeof(int8_t) + sizeof(int8_t) + sizeof(int16_t) + sizeof(int32_t)
 //Size of 1 window update (deleted window) = winId + type of update
@@ -166,6 +193,15 @@ enum WinState{WIN_UNCHANGED, WIN_DELETED, WIN_ICONIFIED};
 #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 EVLENGTH 41
 
@@ -175,6 +211,17 @@ enum WinState{WIN_UNCHANGED, WIN_DELETED, WIN_ICONIFIED};
 //height of screen region
 #define SCREEN_REG_HEIGHT 40
 
+//the distance to determinate if the point belongs to region
+#define MAXDISTANCETOREGION 40
+//regions to split the paint rectangle
+struct PaintRectRegion
+{
+    int x1, x2, y1, y2;
+    struct PaintRectRegion* next;
+    BOOL united;
+};
+
+
 //represents the screen regions for updates
 typedef struct
 {
@@ -213,6 +260,23 @@ struct frame_region
     point_t source_coordinates;
 };
 
+//elemnet of the dgram list
+struct dgram_element
+{
+    unsigned char* data;
+    uint16_t length;
+    struct dgram_element* next;
+};
+
+//resend void request_selection_from_client(enum SelectionType selection)
+struct dgram_request_element
+{
+    uint16_t seq;
+    time_t msec;
+    struct dgram_request_element* next;
+};
+
+
 
 //we can find out cursor type by analyzing size of data.
 //size = 0 - cursor is already sent to client
@@ -395,7 +459,7 @@ struct remoteWindow
 struct _remoteHostVars
 {
     unsigned char compression;
-    OsTimerPtr checkConnectionTimer;
+    OsTimerPtr checkConnectionTimer, checkKeepAliveTimer, sendKeepAliveTimer;
     int agentState;
     BOOL nxagentMode;
     char optionsFile[256];
@@ -415,6 +479,12 @@ struct _remoteHostVars
 
     unsigned long send_thread_id;
 
+    enum ServerType serverType;
+    uint16_t framePacketSeq;
+    uint16_t controlPacketSeq;
+    uint16_t repaintPacketSeq;
+    uint16_t clientEventSeq;
+
     //client information
     enum OS_VERSION client_os;
     uint16_t client_version;
@@ -432,8 +502,9 @@ struct _remoteHostVars
     KdScreenInfo* ephyrScreen;
     uint32_t main_img_height, main_img_width;
 
-//    #warning remove this hack
-    int numofimg;
+/*#warning for debug purposes
+    uint64_t sizeOfRects;
+    uint64_t sizeOfRegions;*/
 
     int clientsock, serversock;
     BOOL rootless;
@@ -442,7 +513,6 @@ struct _remoteHostVars
     screen_region* screen_regions;
     int reg_horiz, reg_vert;
 
-
     struct cache_elem* first_cache_element;
     struct cache_elem* last_cache_element;
 
@@ -469,10 +539,18 @@ struct _remoteHostVars
     struct sentCursor* sentCursorsHead;
     struct sentCursor* sentCursorsTail;
 
+    struct dgram_element* server_control_datagrams;
+    struct dgram_element* client_event_datagrams;
+    struct dgram_request_element *cl_event_resend_request_first, *cl_event_resend_request_last;
+
     struct remoteWindow* windowList;
     BOOL windowsUpdated;
 
     pthread_mutex_t sendqueue_mutex;
+    //mutex for server dgrams_synchronization
+    pthread_mutex_t server_dgram_mutex;
+    //mutex for client dgrams_synchronization
+    pthread_mutex_t client_dgram_mutex;
     pthread_mutex_t mainimg_mutex;
     pthread_cond_t have_sendqueue_cond;
 
@@ -481,6 +559,8 @@ struct _remoteHostVars
 
     BOOL client_connected;
     BOOL client_initialized;
+    //last time when client sent sync packet
+    time_t last_client_keepalive_time;
 
     //if all cache are cleared and notofictaion to client should be send
     BOOL cache_rebuilt;
@@ -488,9 +568,12 @@ struct _remoteHostVars
     struct SelectionStructure selstruct;
 } ;
 
-int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, uint32_t compressed, uint32_t total);
+int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, uint32_t compressed, uint32_t total_sz);
 int send_output_selection(struct OutputChunk* chunk);
 
+int send_packet_as_datagrams(unsigned char* data, uint32_t length, uint8_t dgType);
+void resend_dgrams(char* buffer, int length, uint8_t dgType);
+
 void set_client_version(uint16_t ver, uint16_t os);
 
 void readInputSelectionBuffer(char* buff);
@@ -575,6 +658,8 @@ void remote_set_jpeg_quality(const char* quality);
 const char*  remote_get_init_geometry(void);
 void remote_check_windowstree(WindowPtr root);
 void remote_check_window(WindowPtr win);
+BOOL remote_process_client_event(char* buff, int length);
+void remote_recv_dgram(void);
 struct remoteWindow* remote_find_window(WindowPtr win);
 WindowPtr remote_find_window_on_screen(WindowPtr win, WindowPtr root);
 WindowPtr remote_find_window_on_screen_by_id(uint32_t winId, WindowPtr root);
@@ -587,4 +672,22 @@ void remote_check_rootless_windows_for_updates(KdScreenInfo *screen);
 void markDirtyRegions(uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint8_t jpegQuality, uint32_t winId);
 int getDirtyScreenRegion(void);
 void send_dirty_region(int index);
+void add_dgram_to_list(unsigned char*  dgram, uint16_t length, struct dgram_element** dg_list);
+void remove_dgram_from_list(struct dgram_element** dg_list, uint16_t last_pack_seq, BOOL remove_all);
+void resend_dgram(uint8_t dgType, uint16_t packSeq, uint16_t dgSeq);
+void resend_dgram_packet(uint8_t dgType, uint16_t packSeq);
+unsigned int checkClientAlive(OsTimerPtr timer, CARD32 time_card, void* args);
+unsigned int sendServerAlive(OsTimerPtr timer, CARD32 time_card, void* args);
+void send_srv_disconnect(void);
+BOOL insideOfRegion(struct PaintRectRegion* reg, int x , int y);
+struct PaintRectRegion* findRegionForPoint(struct PaintRectRegion* firstRegion, int x , int y);
+BOOL unitePaintRegions(struct PaintRectRegion* firstRegion);
+void send_sync_failed_notification(void);
+//perform cleanup of all caches and queues when disconnecting or performing reinitialization
+void clean_everything(void);
+void remote_check_event_packet_integrity(uint16_t seq_to_process);
+struct dgram_element* find_dgram_in_list(struct dgram_element** dg_list, uint16_t pack_seq);
+struct dgram_request_element* find_resend_request(uint16_t seq);
+void remove_resend_request(uint16_t seq, BOOL all);
+void resend_frame(uint32_t crc);
 #endif /* X2GOKDRIVE_REMOTE_H */

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


More information about the x2go-commits mailing list