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