[X2Go-Commits] [x2gokdrive] 01/01: support sending and recieving selections on demand. Support reading and writing INCR properties.

git-admin at x2go.org git-admin at x2go.org
Wed Sep 30 18:36:18 CEST 2020


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

x2go pushed a commit to branch master
in repository x2gokdrive.

commit aed1bdfa2e43fdf9fb3bdb576e8cb00d7e11aac3
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date:   Wed Sep 30 11:36:04 2020 -0500

    support sending and recieving selections on demand. Support reading and writing INCR properties.
---
 debian/changelog      |   1 +
 x2gokdriveremote.c    | 352 ++++++++++++++++++----
 x2gokdriveremote.h    |  93 ++++--
 x2gokdriveselection.c | 787 +++++++++++++++++++++++++++++++++++++++++---------
 x2gokdriveselection.h |  25 +-
 5 files changed, 1047 insertions(+), 211 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 3a442cf..9bfc70b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -14,6 +14,7 @@ x2gokdrive (0.0.0.1-0x2go1) UNRELEASED; urgency=medium
     - add xcb-xfixes to deps.
     - send a recive feature versions.
     - reinit client version on new connection and awaka sending thread when client version recieved.
+    - support sending and recieving selections on demand. Support reading and writing INCR properties.
 
   [ Mihai Moldovan ]
   * Initial release:
diff --git a/x2gokdriveremote.c b/x2gokdriveremote.c
index 210de85..0fd828a 100644
--- a/x2gokdriveremote.c
+++ b/x2gokdriveremote.c
@@ -36,6 +36,7 @@
 #endif
 #include "x2gokdriveremote.h"
 #include "x2gokdriveselection.h"
+#include <zlib.h>
 
 /* init it in OsInit() */
 static struct _remoteHostVars remoteVars = {0};
@@ -45,6 +46,7 @@ static BOOL remoteInitialized=FALSE;
 
 void remote_selection_init(void)
 {
+    remoteVars.selstruct.readingInputBuffer=-1;
     selection_init(&remoteVars);
 }
 
@@ -207,6 +209,7 @@ void remote_removeCursor(uint32_t serialNumber)
     struct sentCursor* prev = NULL;
     struct deletedCursor* dcur = NULL;
 
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     cur=remoteVars.sentCursorsHead;
 
@@ -239,6 +242,7 @@ void remote_removeCursor(uint32_t serialNumber)
         remoteVars.first_deleted_cursor=remoteVars.last_deleted_cursor=dcur;
     }
     ++remoteVars.deletedcursor_list_size;
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 }
 
@@ -253,8 +257,10 @@ void remote_sendCursor(CursorPtr cursor)
     cframe->data=0;
     cframe->next=0;
 
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     cursorSent=isCursorSent(cursor->serialNumber);
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     if(!cursorSent)
     {
@@ -289,13 +295,17 @@ void remote_sendCursor(CursorPtr cursor)
         cframe->forG=cursor->foreGreen*255./65535.0;
         cframe->forB=cursor->foreBlue*255./65535.0;
 
+
         pthread_mutex_lock(&remoteVars.sendqueue_mutex);
         addSentCursor(cursor->serialNumber);
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     }
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     addCursorToQueue(cframe);
     pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 }
 
@@ -305,6 +315,7 @@ void remote_sendVersion(void)
     unsigned char buffer[56] = {0};
     _X_UNUSED int l;
 
+
     *((uint32_t*)buffer)=SERVERVERSION; //4B
     *((uint16_t*)buffer+2)=FEATURE_VERSION;
     EPHYR_DBG("Sending server version: %d", FEATURE_VERSION);
@@ -312,6 +323,16 @@ void remote_sendVersion(void)
     remoteVars.server_version_sent=TRUE;
 }
 
+void request_selection_from_client(enum SelectionType selection)
+{
+    unsigned char buffer[56] = {0};
+    _X_UNUSED int l;
+
+    *((uint32_t*)buffer)=DEMANDCLIENTSELECTION; //4B
+    *((uint16_t*)buffer+2)=(uint16_t)selection;
+    l=write(remoteVars.clientsock,buffer,56);
+    EPHYR_DBG("requesting selection from client");
+}
 
 static
 int32_t send_cursor(struct cursorFrame* cursor)
@@ -542,19 +563,19 @@ int send_deleted_cursors(void)
     return sent;
 }
 
-int send_output_selection(outputChunk* chunk)
+int send_output_selection(struct OutputChunk* chunk)
 {
     //client supports extended selections
     if(remoteVars.selstruct.clientSupportsExetndedSelection)
     {
         //send extended selection
-        return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, chunk->firstChunk, chunk->lastChunk, chunk->compressed, chunk->totalSize);
+        return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, chunk->firstChunk, chunk->lastChunk, chunk->compressed_size, chunk->totalSize);
     }
     else
     {
         //older client doesn't support only uncompressed datas in single chunk
         //not sending chunk in other case
-        if(!chunk->compressed && chunk->firstChunk && chunk->lastChunk)
+        if(!chunk->compressed_size && chunk->firstChunk && chunk->lastChunk)
         {
             return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, TRUE, TRUE, FALSE, chunk->size);
         }
@@ -563,7 +584,7 @@ int send_output_selection(outputChunk* chunk)
     return 0;
 }
 
-int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, BOOL 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)
 {
     unsigned char buffer[56] = {0};
     _X_UNUSED int ln = 0;
@@ -578,12 +599,20 @@ 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; //28
+    *((uint32_t*)buffer+7)=total; //32
 
 
 //    #warning check this
     ln=write(remoteVars.clientsock,buffer,56);
 
+    //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)
     {
         l=write(remoteVars.clientsock,data+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
@@ -1339,6 +1368,7 @@ void sendMainImageFromSendThread(uint32_t width, uint32_t height, int32_t dx ,in
         EPHYR_DBG("sending mainImage");
     }
 
+
     pthread_mutex_lock(&remoteVars.mainimg_mutex);
 
     for(int j=0;j<9;++j)
@@ -1387,6 +1417,7 @@ void sendMainImageFromSendThread(uint32_t width, uint32_t height, int32_t dx ,in
         send_frame(width, height,dx,dy,0,regions);
     }
 
+
     pthread_mutex_unlock(&remoteVars.mainimg_mutex);
     free(regions[0].compressed_data);
 }
@@ -1395,6 +1426,7 @@ static
 void *send_frame_thread (void *threadid)
 {
     long tid;
+    enum SelectionType r;
     tid = (long)threadid;
 
     EPHYR_DBG("Started sending thread: #%ld!\n", tid);
@@ -1473,6 +1505,7 @@ void *send_frame_thread (void *threadid)
 
 
 
+
         pthread_mutex_lock(&remoteVars.sendqueue_mutex);
         //only accept one client, close server socket
         shutdown(remoteVars.serversock, SHUT_RDWR);
@@ -1482,8 +1515,7 @@ void *send_frame_thread (void *threadid)
 #endif /* XORG_VERSION_CURRENT */
         remoteVars.client_connected=TRUE;
         remoteVars.server_version_sent=FALSE;
-        remoteVars.client_version=0;
-        remoteVars.client_os=0;
+        set_client_version(0,0);
         if(remoteVars.checkConnectionTimer)
         {
             TimerFree(remoteVars.checkConnectionTimer);
@@ -1495,10 +1527,12 @@ void *send_frame_thread (void *threadid)
         remoteVars.data_copy=0;
         remoteVars.evBufferOffset=0;
         setAgentState(RUNNING);
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 
         while(1)
         {
+
             pthread_mutex_lock(&remoteVars.sendqueue_mutex);
             if(!remoteVars.client_connected)
             {
@@ -1508,6 +1542,7 @@ void *send_frame_thread (void *threadid)
 #endif /* XORG_VERSION_CURRENT */
                 shutdown(remoteVars.clientsock, SHUT_RDWR);
                 close(remoteVars.clientsock);
+
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
                 break;
             }
@@ -1535,12 +1570,13 @@ void *send_frame_thread (void *threadid)
             if(remoteVars.selstruct.firstOutputChunk && !remoteVars.first_sendqueue_element && !remoteVars.firstCursor)
             {
                 //get chunk from queue
-                outputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
-                remoteVars.selstruct.firstOutputChunk=(outputChunk*)chunk->next;
+                struct OutputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+                remoteVars.selstruct.firstOutputChunk=chunk->next;
                 if(!remoteVars.selstruct.firstOutputChunk)
                 {
                     remoteVars.selstruct.lastOutputChunk=NULL;
                 }
+
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
                 //send chunk
                 send_output_selection(chunk);
@@ -1551,9 +1587,28 @@ void *send_frame_thread (void *threadid)
                 }
 //                 EPHYR_DBG(" REMOVE CHUNK %p %p %p", remoteVars.selstruct.firstOutputChunk, remoteVars.selstruct.lastOutputChunk, chunk);
                 free(chunk);
+
                 pthread_mutex_lock(&remoteVars.sendqueue_mutex);
             }
 
+            //check if we need to request the selection from client
+            if(remoteVars.selstruct.requestSelection[PRIMARY] || remoteVars.selstruct.requestSelection[CLIPBOARD])
+            {
+                for(r=PRIMARY; r<=CLIPBOARD; ++r)
+                {
+                    if(remoteVars.selstruct.requestSelection[r])
+                    {
+                        remoteVars.selstruct.requestSelection[r]=FALSE;
+
+                        pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+                        //send request for selection
+                        request_selection_from_client(r);
+
+                        pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+                    }
+                }
+            }
+
             if(remoteVars.firstCursor)
             {
                 /* get cursor from queue, delete it from queue, unlock mutex and send cursor. After sending free cursor */
@@ -1563,6 +1618,7 @@ void *send_frame_thread (void *threadid)
                     remoteVars.firstCursor=remoteVars.firstCursor->next;
                 else
                     remoteVars.firstCursor=remoteVars.lastCursor=0;
+
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
                 send_cursor(cframe);
                 if(cframe->data)
@@ -1572,9 +1628,11 @@ void *send_frame_thread (void *threadid)
             }
             else
             {
+
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
             }
 
+
             pthread_mutex_lock(&remoteVars.sendqueue_mutex);
             if(remoteVars.first_sendqueue_element)
             {
@@ -1614,16 +1672,19 @@ void *send_frame_thread (void *threadid)
                     uint32_t frame_height=frame->height;
 
                     /* unlock sendqueue for main thread */
+
                     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
                     send_frame(frame_width, frame_height, x, y, crc, frame->regions);
                 }
                 else
                 {
                     EPHYR_DBG("Sending main image or screen update");
+
                     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
                     sendMainImageFromSendThread(width, height, x, y);
                 }
 
+
                 pthread_mutex_lock(&remoteVars.sendqueue_mutex);
                 if(frame)
                 {
@@ -1665,11 +1726,14 @@ void *send_frame_thread (void *threadid)
                     send_deleted_cursors();
                 }
 
+
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
                 remoteVars.framenum++;
             }
             else
             {
+
+
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
             }
         }
@@ -1682,13 +1746,13 @@ void *send_frame_thread (void *threadid)
 /* warning! sendqueue_mutex should be locked by thread calling this function! */
 void clear_output_selection(void)
 {
-    outputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
-    outputChunk* prev_chunk;
+    struct OutputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+    struct OutputChunk* prev_chunk;
 
     while(chunk)
     {
         prev_chunk=chunk;
-        chunk=(outputChunk*)chunk->next;
+        chunk=chunk->next;
         if(prev_chunk->data)
         {
             free(prev_chunk->data);
@@ -1852,6 +1916,7 @@ void setAgentState(int state)
 void disconnect_client(void)
 {
     EPHYR_DBG("DISCONNECTING CLIENT, DOING SOME CLEAN UP");
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     remoteVars.client_connected=FALSE;
     setAgentState(SUSPENDED);
@@ -1860,44 +1925,113 @@ void disconnect_client(void)
     freeCursors();
     clear_output_selection();
     pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 }
 
+void unpack_current_chunk_to_buffer(struct InputBuffer* selbuff)
+{
+    //unpacking the data from current chunk to selbuff
+
+    z_stream stream;
+    stream.zalloc = Z_NULL;
+    stream.zfree = Z_NULL;
+    stream.opaque = Z_NULL;
+
+//     EPHYR_DBG("inflate %d bytes to %d", selbuff->currentChunkCompressedSize, selbuff->currentChunkSize);
+
+    stream.avail_in = selbuff->currentChunkCompressedSize;
+    stream.next_in = selbuff->currentChunkCompressedData;
+    stream.avail_out = selbuff->currentChunkSize;
+    stream.next_out = selbuff->data + selbuff->bytesReady;
+
+    inflateInit(&stream);
+    inflate(&stream, Z_NO_FLUSH);
+    inflateEnd(&stream);
+
+    if(stream.total_out != selbuff->currentChunkSize)
+    {
+        //something is wrong with extracting the data
+        EPHYR_DBG("WARNING!!!! extracting the data failed output has %d bytes instead of %d", (uint32_t)stream.total_out, selbuff->currentChunkSize);
+    }
+
+
+//     EPHYR_DBG("%s", selbuff->data + selbuff->bytesReady);
+    ///freeing compressed data
+    free(selbuff->currentChunkCompressedData);
+    selbuff->currentChunkCompressedData=NULL;
+
+    selbuff->bytesReady+=selbuff->currentChunkSize;
+    selbuff->currentChunkCompressedSize=0;
+}
+
 
 void readInputSelectionBuffer(char* buff)
 {
     //read th rest of the chunk data
 
-    inputBuffer* selbuff = &remoteVars.selstruct.inSelection[remoteVars.selstruct.readingInputBuffer];
+    struct InputBuffer* selbuff;
+    int leftToRead, l;
 
-    int leftToRead=selbuff->currentChunkSize - selbuff->currentChunkBytesReady;
-
-
-    int l=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
 
     //lock input selection
+
     pthread_mutex_lock(&remoteVars.selstruct.inMutex);
 
-    //copy data to selection
-    memcpy(selbuff->data+selbuff->bytesReady, buff, l);
-    selbuff->bytesReady+=l;
-    selbuff->currentChunkBytesReady+=l;
+    selbuff = &remoteVars.selstruct.inSelection[remoteVars.selstruct.readingInputBuffer];
+
+    //if the data is not compressed read it directly to the buffer
+    if(!selbuff->currentChunkCompressedSize)
+    {
+        leftToRead=selbuff->currentChunkSize - selbuff->currentChunkBytesReady;
+        l=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
+
+        //copy data to selection
+        memcpy(selbuff->data+selbuff->bytesReady, buff, l);
+        selbuff->bytesReady+=l;
+        selbuff->currentChunkBytesReady+=l;
+        if(selbuff->currentChunkBytesReady==selbuff->currentChunkSize)
+        {
+            //selection chunk received completely, next event will start with event header
+            //         EPHYR_DBG("READY Selection Chunk, read %d",selbuff->currentChunkSize);
+            remoteVars.selstruct.readingInputBuffer=-1;
+        }
+    }
+    else
+    {
+        //copy to the buffer for compressed data
+        leftToRead=selbuff->currentChunkCompressedSize - selbuff->currentChunkBytesReady;
+        l=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
+
+        //copy data to selection
+        memcpy(selbuff->currentChunkCompressedData+selbuff->currentChunkBytesReady, buff, l);
+        selbuff->currentChunkBytesReady+=l;
+        if(selbuff->currentChunkBytesReady==selbuff->currentChunkCompressedSize)
+        {
+            //selection chunk received completely, next event will start with event header
+            EPHYR_DBG("READY Selection Chunk, read %d",selbuff->currentChunkSize);
+            remoteVars.selstruct.readingInputBuffer=-1;
+            //unpack data to selection buffer
+            unpack_current_chunk_to_buffer(selbuff);
+        }
+    }
 
     if(selbuff->bytesReady==selbuff->size)
     {
         //selection buffer received completely
 //         EPHYR_DBG("READY Selection %d, MIME %d, Read %d from %d", remoteVars.selstruct.readingInputBuffer, selbuff->mimeData, selbuff->bytesReady, selbuff->size);
         //send notify to system that we are using selection
-        own_selection(remoteVars.selstruct.readingInputBuffer);
-
-    }
-    if(selbuff->currentChunkBytesReady==selbuff->currentChunkSize)
-    {
-        //selection chunk received completely, next event will start with event header
-//         EPHYR_DBG("READY Selection Chunk, read %d",selbuff->currentChunkSize);
-        remoteVars.selstruct.readingInputBuffer=-1;
+        //if state is requested we already own this selection after notify
+        if(selbuff->state != REQUESTED)
+            own_selection(remoteVars.selstruct.readingInputBuffer);
+        selbuff->state=COMPLETED;
+        //send notification event to interrupt sleeping selection thread
+        client_sel_data_notify(remoteVars.selstruct.readingInputBuffer);
     }
     //unlock selection
+
+
     pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
 }
 
@@ -1910,9 +2044,9 @@ void readInputSelectionHeader(char* buff)
 
     uint32_t size, totalSize;
     uint8_t destination, mime;
-    inputBuffer* selbuff = NULL;
+    struct InputBuffer* selbuff = NULL;
     BOOL firstChunk=FALSE, lastChunk=FALSE;
-    BOOL compressed=FALSE;
+    uint32_t compressedSize=0;
     uint32_t headerSize=10;
     uint32_t l;
 
@@ -1923,20 +2057,21 @@ void readInputSelectionHeader(char* buff)
     //if client supports ext selection, read extended header
     if(remoteVars.selstruct.clientSupportsExetndedSelection)
     {
-        headerSize=17;
+        headerSize=20;
         firstChunk=*((uint8_t*)buff + 10);
         lastChunk=*((uint8_t*)buff + 11);
-        compressed=*((uint8_t*)buff + 12);
-        totalSize=*( (uint32_t*) (buff+13));
+        compressedSize=*((uint32_t*) (buff + 12));
+        totalSize=*( (uint32_t*) (buff+16));
     }
     else
     {
-        compressed=FALSE;
+        compressedSize=0;
         lastChunk=firstChunk=TRUE;
         totalSize=size;
     }
 
-//     EPHYR_DBG("HAVE NEW INCOMING SELECTION Chunk: sel %d size %d mime %d",destination, size, mime);
+     EPHYR_DBG("HAVE NEW INCOMING SELECTION Chunk: sel %d size %d mime %d compressed size %d, total %d",destination, size, mime, compressedSize, totalSize);
+
 
     //lock selection
     pthread_mutex_lock(&remoteVars.selstruct.inMutex);
@@ -1944,6 +2079,28 @@ void readInputSelectionHeader(char* buff)
     remoteVars.selstruct.readingInputBuffer=-1;
 
     selbuff = &remoteVars.selstruct.inSelection[destination];
+
+    //we recieved selection notify from client
+    if(firstChunk && lastChunk && remoteVars.selstruct.clientSupportsExetndedSelection && (totalSize == 0) &&(size == 0))
+    {
+        EPHYR_DBG("Selection notify from client for %d", destination);
+        if(selbuff->size && selbuff->data)
+        {
+            free(selbuff->data);
+        }
+        selbuff->size=0;
+        selbuff->mimeData=mime;
+        selbuff->data=0;
+        selbuff->bytesReady=0;
+        selbuff->state=NOTIFIED;
+// own selection
+        own_selection(destination);
+        //unlock mutex
+
+
+        pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
+        return;
+    }
     if(firstChunk)
     {
         //if it's first chunk, initialize our selection buffer
@@ -1957,30 +2114,73 @@ void readInputSelectionHeader(char* buff)
         selbuff->bytesReady=0;
     }
 
-    //read the selection data from header
-    l=(size < EVLENGTH-headerSize)?size:(EVLENGTH-headerSize);
-    memcpy(selbuff->data+selbuff->bytesReady, buff+headerSize, l);
+    if(selbuff->currentChunkCompressedSize && selbuff->currentChunkCompressedData)
+    {
+        free(selbuff->currentChunkCompressedData);
+    }
+    selbuff->currentChunkCompressedData=NULL;
+    selbuff->currentChunkCompressedSize=0;
 
-    selbuff->bytesReady+=l;
+    //if compressed data will be read in buffer for compressed data
+    if(compressedSize)
+    {
+        selbuff->currentChunkCompressedData=malloc(compressedSize);
+        selbuff->currentChunkCompressedSize=compressedSize;
+        l=(compressedSize < EVLENGTH-headerSize)?compressedSize:(EVLENGTH-headerSize);
+        memcpy(selbuff->currentChunkCompressedData, buff+headerSize, l);
+    }
+    else
+    {
+        //read the selection data from header
+        l=(size < EVLENGTH-headerSize)?size:(EVLENGTH-headerSize);
+        memcpy(selbuff->data+selbuff->bytesReady, buff+headerSize, l);
+
+        selbuff->bytesReady+=l;
+    }
 
     selbuff->currentChunkBytesReady=l;
     selbuff->currentChunkSize=size;
 
+
+    if(!compressedSize)
+    {
+        if(selbuff->currentChunkBytesReady != selbuff->currentChunkSize)
+        {
+            // we didn't recieve complete chunk yet, next event will have data
+            remoteVars.selstruct.readingInputBuffer=destination;
+        }
+    }
+    else
+    {
+        if(selbuff->currentChunkBytesReady != selbuff->currentChunkCompressedSize)
+        {
+            // we didn't recieve complete chunk yet, next event will have data
+            remoteVars.selstruct.readingInputBuffer=destination;
+        }
+        else
+        {
+            //we read all compressed chunk data, unpack it to sel buff
+            unpack_current_chunk_to_buffer(selbuff);
+        }
+
+    }
+
     if(selbuff->size == selbuff->bytesReady)
     {
         //Selection is completed
 //         EPHYR_DBG("READY INCOMING SELECTION for %d",destination);
         //own the selection
-        own_selection(destination);
+        //if state is requested we already own this selection after notify
+        if(selbuff->state != REQUESTED)
+           own_selection(destination);
+        selbuff->state=COMPLETED;
+        //send notification event to interrupt sleeping selection thread
+        client_sel_data_notify(destination);
     }
 
-    if(selbuff->currentChunkBytesReady != selbuff->currentChunkSize)
-    {
-        // we didn't recieve complete chunk yet, next event will have data
-        remoteVars.selstruct.readingInputBuffer=destination;
-//         EPHYR_DBG("READ INCOMING BUFFER %d, read %d from %d", destination, selbuff->bytesReady, selbuff->size);
-    }
     //unlock selection
+
+
     pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
 }
 
@@ -1994,6 +2194,8 @@ clientReadNotify(int fd, int ready, void *data)
 
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     con=remoteVars.client_connected;
+
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     if(!con)
         return;
@@ -2176,12 +2378,14 @@ clientReadNotify(int fd, int ready, void *data)
 
                     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);
                     }
                     else
                     {
                         EPHYR_DBG("UPDATE: skip request");
+
                         pthread_mutex_unlock(&remoteVars.mainimg_mutex);
                     }
                     break;
@@ -2195,17 +2399,18 @@ clientReadNotify(int fd, int ready, void *data)
                 {
                     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);
-                    remoteVars.client_version=ver;
-                    if(os > OS_DARWIN)
-                    {
-                        EPHYR_DBG("Unsupported OS, assuming OS_LINUX");
-                    }
-                    else
-                        remoteVars.client_os=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;
+                }
                 default:
                 {
                     EPHYR_DBG("UNSUPPORTED EVENT: %d",event_type);
@@ -2227,6 +2432,22 @@ clientReadNotify(int fd, int ready, void *data)
 
 }
 
+void set_client_version(uint16_t ver, uint16_t os)
+{
+    remoteVars.client_version=ver;
+    if(os > OS_DARWIN)
+    {
+        EPHYR_DBG("Unsupported OS, assuming OS_LINUX");
+    }
+    else
+        remoteVars.client_os=os;
+    //clients version >= 1 supporting extended selection (sending big amount of data aín several chunks)
+    remoteVars.selstruct.clientSupportsExetndedSelection=(ver > 1);
+    //Linux clients supporting sending selection on demand (not sending data if not needed)
+    remoteVars.selstruct.clientSupportsOnDemandSelection=((ver > 1) && (os == OS_LINUX));
+
+}
+
 #if XORG_VERSION_CURRENT < 11900000
 void pollEvents(void)
 {
@@ -2234,8 +2455,10 @@ void pollEvents(void)
     struct pollfd fds[2];
     int    nfds = 1;
     BOOL con;
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     con=remoteVars.client_connected;
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     if(!con)
         return;
@@ -2254,6 +2477,7 @@ unsigned int checkSocketConnection(OsTimerPtr timer, CARD32 time, void* args)
 {
     EPHYR_DBG("CHECKING ACCEPTED CONNECTION");
     TimerFree(timer);
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     remoteVars.checkConnectionTimer=0;
     if(!remoteVars.client_connected)
@@ -2265,6 +2489,7 @@ unsigned int checkSocketConnection(OsTimerPtr timer, CARD32 time, void* args)
     {
         EPHYR_DBG("CLIENT CONNECTED");
     }
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     return 0;
 }
@@ -2333,6 +2558,7 @@ void terminateServer(int exitStatus)
     if(remoteVars.selstruct.selThreadId)
     {
         pthread_cancel(remoteVars.selstruct.selThreadId);
+        remove_obsolete_incr_transactions(FALSE);
         if(remoteVars.selstruct.xcbConnection)
         {
             xcb_disconnect(remoteVars.selstruct.xcbConnection);
@@ -2718,6 +2944,7 @@ struct cache_elem* add_cache_element(uint32_t crc, int32_t dx, int32_t dy, uint3
                 ++i;
             }
         }
+
         pthread_mutex_unlock(&remoteVars.mainimg_mutex);
 
         el->rval/=numOfPix;
@@ -2798,6 +3025,7 @@ void initFrameRegions(struct cache_elem* frame)
         uint32_t bestm_crc = 0;
         struct cache_elem* best_match = NULL;
 
+
         pthread_mutex_lock(&remoteVars.sendqueue_mutex);
         best_match = find_best_match(frame, &match_val);
 
@@ -2805,6 +3033,7 @@ void initFrameRegions(struct cache_elem* frame)
         {
             best_match->busy+=1;
         }
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 
         if(best_match && best_match->width>4 && best_match->height>4 && best_match->width * best_match->height > 100 )
@@ -2916,12 +3145,15 @@ void initFrameRegions(struct cache_elem* frame)
             }
         }
         /* if we didn't find any common regions and have best match element, mark it as not busy */
+
+
         pthread_mutex_lock(&remoteVars.sendqueue_mutex);
         if(best_match && frame->source != best_match)
         {
 //            EPHYR_DBG("Have best mutch but not common region");
             best_match->busy-=1;
         }
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 
     }
@@ -3002,10 +3234,12 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
     struct cache_elem* frame = 0;
     struct sendqueue_element* element = NULL;
 
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     if(! (remoteVars.client_connected && remoteVars.client_initialized))
     {
         /* don't have any clients connected, return */
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
         return;
     }
@@ -3013,6 +3247,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
     if(crc==0)
     {
         /* sending main image */
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
     }
     else
@@ -3031,6 +3266,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
         }
         frame->busy+=1;
 
+
         pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 
         /* if element is new find common regions and compress the data */
@@ -3041,6 +3277,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
         }
     }
 
+
     pthread_mutex_lock(&remoteVars.sendqueue_mutex);
     /* add element in the queue for sending */
     element=malloc(sizeof(struct sendqueue_element));
@@ -3060,6 +3297,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
         remoteVars.first_sendqueue_element=remoteVars.last_sendqueue_element=element;
     }
     pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 
     /* on this point will be sent wakeup single to send mutex */
@@ -3104,6 +3342,7 @@ remote_paint_rect(KdScreenInfo *screen,
          * OK, here we assuming that XSERVERBPP is 4. If not, we'll have troubles
          * but it should work faster like this
          */
+
         pthread_mutex_lock(&remoteVars.mainimg_mutex);
 
         maxdiff=2;
@@ -3169,6 +3408,7 @@ remote_paint_rect(KdScreenInfo *screen,
                 ind+=XSERVERBPP;
             }
         }
+
         pthread_mutex_unlock(&remoteVars.mainimg_mutex);
 
 
@@ -3202,11 +3442,13 @@ uint32_t calculate_crc(uint32_t width, uint32_t height, int32_t dx, int32_t dy)
 {
     uint32_t crc=adler32(0L, Z_NULL, 0);
 
+
     pthread_mutex_lock(&remoteVars.mainimg_mutex);
     for(uint32_t y=0; y< height;++y)
     {
         crc=adler32(crc,remoteVars.main_img+ ((y+dy)*remoteVars.main_img_width + dx)*XSERVERBPP, width*XSERVERBPP);
     }
+
     pthread_mutex_unlock(&remoteVars.mainimg_mutex);
     return crc;
 }
@@ -3249,6 +3491,7 @@ remote_screen_init(KdScreenInfo *screen,
     EPHYR_DBG("host_screen=%p x=%d, y=%d, wxh=%dx%d, buffer_height=%d",
               screen, x, y, width, height, buffer_height);
 
+
     pthread_mutex_lock(&remoteVars.mainimg_mutex);
 
 
@@ -3294,6 +3537,7 @@ remote_screen_init(KdScreenInfo *screen,
 
     *bytes_per_line = width*XSERVERBPP;
     *bits_per_pixel = 32;
+
     pthread_mutex_unlock(&remoteVars.mainimg_mutex);
 
     return remoteVars.main_img;
diff --git a/x2gokdriveremote.h b/x2gokdriveremote.h
index 4249563..7735d0b 100644
--- a/x2gokdriveremote.h
+++ b/x2gokdriveremote.h
@@ -95,7 +95,8 @@
 //FEATURE_VERSION is not cooresponding to actual version of server
 //it used to tell server which features are supported by server
 //Changes 0 - 1: sending and recieving client and OS version
-#define FEATURE_VERSION 1
+//Changes 1 - 2: supporting extended selection and sending selection on demand
+#define FEATURE_VERSION 2
 
 
 #define EPHYR_WANT_DEBUG 1
@@ -130,7 +131,7 @@ fprintf(stderr, __FILE__ ":%d,%s() " x "\n", __LINE__, __func__, ##a)
 //always 4
 #define XSERVERBPP 4
 
-enum msg_type{FRAME,DELETED, CURSOR, DELETEDCURSOR, SELECTION, SERVERVERSION};
+enum msg_type{FRAME,DELETED, CURSOR, DELETEDCURSOR, SELECTION, SERVERVERSION, DEMANDCLIENTSELECTION};
 enum AgentState{STARTING, RUNNING, RESUMING, SUSPENDING, SUSPENDED, TERMINATING, TERMINATED};
 enum Compressions{JPEG,PNG};
 enum SelectionType{PRIMARY,CLIPBOARD};
@@ -160,6 +161,7 @@ enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
 #define UPDATE 8
 #define SELECTIONEVENT 9
 #define CLIENTVERSION 10
+#define DEMANDSELECTION 11
 
 #define EVLENGTH 41
 
@@ -267,35 +269,59 @@ struct sendqueue_element
     struct sendqueue_element* next;
 };
 
+//chunk of data with output selection
+struct OutputChunk
+{
+    unsigned char* data; //data
+    uint32_t size; //size of chunk in B
+    enum SelectionMime mimeData; //UTF_STRING or PIXMAP (text or image)
+    uint32_t compressed_size; // if chunk is compressed the size of compressed data, otherwise 0
+    BOOL firstChunk; // if it's a first chunk in selection
+    BOOL lastChunk;  // if it's a last chunk in selection
+    enum SelectionType selection; //PRIMARY or CLIPBOARD
+    uint32_t totalSize; //the total size of the selection data
+    struct OutputChunk* next; //next chunk in the queue
+};
+
 //input selection
-typedef struct
+struct InputBuffer
 {
     unsigned char* data; //data
     uint32_t size; //total size of selection
     uint32_t bytesReady; //how many bytes already read
     uint32_t currentChunkSize; //size of chunk we reading now;
     uint32_t currentChunkBytesReady; //how many bytes of current chunk are ready;
+    uint32_t currentChunkCompressedSize; //if chunk is compressed, size of compressed data
+    unsigned char* currentChunkCompressedData; //if chunk is compressed, compressed dat will be stored here
     enum SelectionMime mimeData; //UTF_STRING or PIXMAP
     xcb_timestamp_t timestamp; //ts when we own selection
     BOOL owner; //if we are the owners of selection
-}inputBuffer;
+    enum {NOTIFIED, REQUESTED, COMPLETED} state;
+};
 
+//requests which processing should be delayed till we get data from client
+struct DelayedRequest
+{
+    xcb_selection_request_event_t *request; // request from X-client
+    xcb_selection_notify_event_t* event; // event which we are going to send to X-client
+    struct DelayedRequest* next;
+};
 
-//chunk of data with output selection
-typedef struct
+//save running INCR transactions in this struct
+struct IncrTransaction
 {
-    unsigned char* data; //data
-    uint32_t size; //size of chunk in B
-    enum SelectionMime mimeData; //UTF_STRING or PIXMAP (text or image)
-    BOOL compressed; // if chunk is compressed
-    BOOL firstChunk; // if it's a first chunk in selection
-    BOOL lastChunk;  // if it's a last chunk in selection
-    enum SelectionType selection; //PRIMARY or CLIPBOARD
-    uint32_t totalSize; //the total size of the selection data
-    struct outputChunk* next; //next chunk in the queue
-}outputChunk;
+    xcb_window_t requestor;
+    xcb_atom_t property;
+    xcb_atom_t target;
+    char* data;
+    uint32_t size;
+    uint32_t sentBytes;
+    xcb_timestamp_t timestamp;
+    struct IncrTransaction* next;
+};
 
-typedef struct
+
+struct SelectionStructure
 {
     unsigned long selThreadId; //id of selection thread
     enum ClipboardMode selectionMode; //CLIP_NONE, CLIP_CLIENT, CLIP_SERVER, CLIP_BOTH
@@ -306,17 +332,29 @@ typedef struct
     xcb_window_t clipWinId; // win id of clipboard window
     xcb_connection_t* xcbConnection; //XCB connection
     BOOL threadStarted; //if selection thread already started
-    BOOL clientSupportsExetndedSelection; //if client supports extended selection
+    BOOL clientSupportsExetndedSelection; //if client supports extended selection - sending selection in several chunks for big size data
+    BOOL clientSupportsOnDemandSelection; //if client supports selection on demand - sending data only if client requests it
     xcb_atom_t incrAtom; //mime type of the incr selection we are reading
     xcb_atom_t currentSelection; //selection we are currently reading
-    outputChunk* firstOutputChunk; //the first and last elements of the
-    outputChunk* lastOutputChunk;  //queue of selection chunks
+    struct OutputChunk* firstOutputChunk; //the first and last elements of the
+    struct OutputChunk* lastOutputChunk;  //queue of selection chunks
+    xcb_atom_t best_atom[2]; //the best mime type for selection to request on demand sel request from client
+    BOOL requestSelection[2]; //selection thread will set it to TRUE if the selection need to be requested
 
     //Input selection members
     int readingInputBuffer; //which selection are reading input buffer at the moments: PRIMARY, CLIPBOARD or -1 if none
-    inputBuffer inSelection[2]; //PRIMARY an CLIPBOARD selection buffers
+    struct InputBuffer inSelection[2]; //PRIMARY an CLIPBOARD selection buffers
+
+    //list of delayed requests
+    struct DelayedRequest* firstDelayedRequest;
+    struct DelayedRequest* lastDelayedRequest;
+
+    //list of INCR transactions
+    struct IncrTransaction* firstIncrTransaction;
+    struct IncrTransaction* lastIncrTransaction;
+
     pthread_mutex_t inMutex; //mutex for synchronization of incoming selection
-}SelectionStructure;
+};
 
 
 struct _remoteHostVars
@@ -400,11 +438,13 @@ struct _remoteHostVars
     BOOL client_connected;
     BOOL client_initialized;
 
-    SelectionStructure selstruct;
+    struct SelectionStructure selstruct;
 } RemoteHostVars;
 
-int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, BOOL compressed, uint32_t total);
-int send_output_selection(outputChunk* chunk);
+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_output_selection(struct OutputChunk* chunk);
+
+void set_client_version(uint16_t ver, uint16_t os);
 
 void readInputSelectionBuffer(char* buff);
 void readInputSelectionHeader(char* buff);
@@ -430,6 +470,7 @@ void setAgentState(int state);
 void terminateServer(int exitStatus);
 
 
+void unpack_current_chunk_to_buffer(struct InputBuffer* selbuff);
 
 unsigned char* image_compress(uint32_t image_width, uint32_t image_height,
                              unsigned char* RGBA_buffer, uint32_t* compressed_size, int bpp, char* fname);
@@ -477,4 +518,6 @@ void
 remote_paint_rect(KdScreenInfo *screen,
                   int sx, int sy, int dx, int dy, int width, int height);
 
+void request_selection_from_client(enum SelectionType selection);
+
 #endif /* X2GOKDRIVE_REMOTE_H */
diff --git a/x2gokdriveselection.c b/x2gokdriveselection.c
index e174aff..0a1f8d4 100644
--- a/x2gokdriveselection.c
+++ b/x2gokdriveselection.c
@@ -34,7 +34,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <png.h>
-
+#include <zlib.h>
 
 #ifdef HAVE_CONFIG_H
 #include <dix-config.h>
@@ -45,9 +45,37 @@
 
 #endif
 #include "x2gokdriveselection.h"
+
+#define SELECTION_DELAY 30000 //timeout for selection operation
+#define INCR_SIZE 256*1024 //size of part for incr selection incr selection
+
 static struct _remoteHostVars *remoteVars = NULL;
 
+//internal atoms
+static xcb_atom_t ATOM_ATOM;
 static xcb_atom_t ATOM_CLIPBOARD;
+static xcb_atom_t ATOM_TARGETS;
+static xcb_atom_t ATOM_TIMESTAMP;
+static xcb_atom_t ATOM_INCR;
+static xcb_atom_t ATOM_XT_SELECTION;
+static xcb_atom_t ATOM_QT_SELECTION;
+
+
+//text atoms
+static xcb_atom_t ATOM_UTF8_STRING;
+static xcb_atom_t ATOM_TEXT_PLAIN_UTF;
+static xcb_atom_t ATOM_STRING;
+static xcb_atom_t ATOM_TEXT;
+static xcb_atom_t ATOM_TEXT_PLAIN;
+
+//image atoms
+static xcb_atom_t ATOM_IMAGE_PNG;
+static xcb_atom_t ATOM_IMAGE_XPM;
+static xcb_atom_t ATOM_IMAGE_JPG;
+static xcb_atom_t ATOM_IMAGE_JPEG;
+static xcb_atom_t ATOM_PIXMAP;
+static xcb_atom_t ATOM_IMAGE_BMP;
+
 
 uint32_t max_chunk(void)
 {
@@ -57,8 +85,248 @@ uint32_t max_chunk(void)
         return 10*1024*1024/4; //10MB
 }
 
+void init_atoms(void)
+{
+    //init intern atoms
+
+    ATOM_ATOM=string_to_atom("ATOM");
+    ATOM_CLIPBOARD=string_to_atom("CLIPBOARD");
+    ATOM_TARGETS=string_to_atom("TARGETS");
+    ATOM_TIMESTAMP=string_to_atom("TIMESTAMP");
+    ATOM_INCR=string_to_atom("INCR");
+
+    ATOM_XT_SELECTION=string_to_atom("_XT_SELECTION_0");
+    ATOM_QT_SELECTION=string_to_atom("_QT_SELECTION");
+
+
+    ATOM_UTF8_STRING=string_to_atom("UTF8_STRING");
+    ATOM_TEXT_PLAIN_UTF=string_to_atom("text/plain;charset=utf-8");
+    ATOM_STRING=string_to_atom("STRING");
+    ATOM_TEXT=string_to_atom("TEXT");
+    ATOM_TEXT_PLAIN=string_to_atom("text/plain");
+
+    ATOM_IMAGE_PNG=string_to_atom("image/png");
+    ATOM_IMAGE_XPM=string_to_atom("image/xpm");
+    ATOM_IMAGE_JPG=string_to_atom("image/jpg");
+    ATOM_IMAGE_JPEG=string_to_atom("image/jpeg");
+    ATOM_PIXMAP=string_to_atom("PIXMAP");
+    ATOM_IMAGE_BMP=string_to_atom("image/bmp");
+}
+
+struct DelayedRequest* discard_delayed_request(struct DelayedRequest* d, struct DelayedRequest* prev)
+{
+    //this function finalizing delayed request and destroys XCB request and event
+    //removing the request from list and returning the pointer of the next element for iteration
+    struct DelayedRequest* next=d->next;
+    xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, d->request->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)d->event);
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+    free(d->event);
+    free((xcb_generic_event_t *)(d->request));
+
+    //remove element from list
+    if(prev)
+        prev->next=next;
+
+    if(d==remoteVars->selstruct.firstDelayedRequest)
+        remoteVars->selstruct.firstDelayedRequest=next;
+
+    if(d==remoteVars->selstruct.lastDelayedRequest)
+        remoteVars->selstruct.lastDelayedRequest=prev;
+
+    free(d);
+
+    return next;
+}
+
+void destroy_incr_transaction(struct IncrTransaction* tr, struct IncrTransaction* prev)
+{
+    //destroy incr transaction
+    struct IncrTransaction* next=tr->next;
+
+    const uint32_t mask[] = { XCB_EVENT_MASK_NO_EVENT };
+    //don't resive property notify events for this window anymore
+    xcb_change_window_attributes(remoteVars->selstruct.xcbConnection, tr->requestor,
+                                 XCB_CW_EVENT_MASK, mask);
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+
+    //free data
+    free(tr->data);
+
+    //remove element from list
+    if(prev)
+        prev->next=next;
+
+    if(tr==remoteVars->selstruct.firstIncrTransaction)
+        remoteVars->selstruct.firstIncrTransaction=next;
+
+    if(tr==remoteVars->selstruct.lastIncrTransaction)
+        remoteVars->selstruct.lastIncrTransaction=prev;
+
+    free(tr);
+}
+
+
+
+BOOL check_req_sanity(xcb_selection_request_event_t* req)
+{
+    //check if the requested mime can be delivered
+    //inmutex should be locked from calling function
+
+    enum SelectionType sel=selection_from_atom(req->selection);
+    if(remoteVars->selstruct.inSelection[sel].mimeData==UTF_STRING)
+    {
+        //if it's one of supported text formats send without convertion
+        if(is_string_atom(req->target))
+        {
+            return TRUE;
+        }
+        else
+        {
+            EPHYR_DBG("Unsupported property requested %d", req->target);
+            return FALSE;
+        }
+    }
+    else
+    {
+        if(!is_image_atom(req->target))
+        {
+            EPHYR_DBG("Unsupported property requested %d", req->target);
+            return FALSE;
+        }
+        return TRUE;
+    }
+}
+
+
+void remove_obsolete_incr_transactions( BOOL checkTs)
+{
+    //remove_obsolete_incr_transactions
+    //if checkTS true, check timestamp and destroy only if ts exceed delay
+
+    struct DelayedRequest* prev=NULL;
+    struct DelayedRequest* d=remoteVars->selstruct.firstDelayedRequest;
+    while(d)
+    {
+        if(!checkTs || (currentTime.milliseconds > (d->request->time + SELECTION_DELAY)))
+        {
+            d=discard_delayed_request(d, prev);
+        }
+        else
+        {
+            prev=d;
+            d=d->next;
+        }
+    }
+}
+
+
+void process_delayed_requests(void)
+{
+    //process delayed requests
+
+    enum SelectionType selection;
+
+    struct DelayedRequest* prev=NULL;
+    struct DelayedRequest* d=remoteVars->selstruct.firstDelayedRequest;
+    while(d)
+    {
+        selection = selection_from_atom( d->request->selection);
+        if(currentTime.milliseconds > (d->request->time + SELECTION_DELAY))
+        {
+            EPHYR_DBG("timeout selection: %d",selection);
+            d=discard_delayed_request(d, prev);
+            continue;
+        }
+        pthread_mutex_lock(&remoteVars->selstruct.inMutex);
+        if(!remoteVars->selstruct.inSelection[selection].owner)
+        {
+
+            pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+            EPHYR_DBG("we are not owner of requested selection %d",selection);
+            //we are not anymore owners of this selection
+            d=discard_delayed_request(d, prev);
+            continue;
+        }
+        if(remoteVars->selstruct.inSelection[selection].timestamp > d->request->time )
+        {
+
+            pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+            EPHYR_DBG("selection request for %d is too old", selection);
+            //requested selection is older than the current one
+            d=discard_delayed_request(d, prev);
+            continue;
+        }
+        if(!check_req_sanity(d->request))
+        {
+            pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+//             EPHYR_DBG("can't convert selection %d to requested myme type %d",selection, d->request->property);
+            //our selection don't support requested mime type
+            d=discard_delayed_request(d, prev);
+            continue;
+        }
+        if(remoteVars->selstruct.inSelection[selection].state != COMPLETED)
+        {
+
+            pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+            //we don't have the data yet
+            prev=d;
+            d=d->next;
+            continue;
+        }
+        //data is ready, send data to requester and discard the request
+        //inMutex need be locked for sending data
+        d->event->property=send_data(d->request);
+
+        pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+        d=discard_delayed_request(d, prev);
+    }
+}
+
+void process_incr_transaction_property(xcb_property_notify_event_t * pn)
+{
+    //process incr transactions
+
+    struct IncrTransaction* prev=NULL;
+    struct IncrTransaction* tr=remoteVars->selstruct.firstIncrTransaction;
+    uint32_t left, sendingBytes;
+    while(tr)
+    {
+        if((tr->requestor == pn->window) && (tr->property == pn->atom ) && ( pn->state == XCB_PROPERTY_DELETE) )
+        {
+            //requestor ready for the new portion of data
+            left=tr->size-tr->sentBytes;
+            if(!left)
+            {
+//                 EPHYR_DBG("all INCR data sent to %d",tr->requestor);
+                //all data sent, sending NULL data and destroying transaction
+                xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, tr->requestor, tr->property,
+                                    tr->target, 8, 0, NULL);
+                xcb_flush(remoteVars->selstruct.xcbConnection);
+                destroy_incr_transaction(tr, prev);
+                return;
+            }
+            sendingBytes=(INCR_SIZE< left)?INCR_SIZE:left;
+
+            xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, tr->requestor, tr->property,
+                                tr->target, 8, sendingBytes, tr->data + tr->sentBytes);
+            xcb_flush(remoteVars->selstruct.xcbConnection);
+            tr->sentBytes+=sendingBytes;
+            tr->timestamp=currentTime.milliseconds;
+            return;
+        }
+        prev=tr;
+        tr=tr->next;
+    }
+    //notify event doesn't belong to any of started incr transactions or it's notification for new property
+    return;
+}
+
+
+
 int own_selection(enum SelectionType selection)
 {
+    //owning selection
+    //remoteVars.selstruct.inMutex locked in the calling function
     xcb_atom_t sel=XCB_ATOM_PRIMARY;
     if(remoteVars->selstruct.selectionMode == CLIP_NONE || remoteVars->selstruct.selectionMode == CLIP_SERVER)
     {
@@ -72,9 +340,12 @@ int own_selection(enum SelectionType selection)
     xcb_set_selection_owner(remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, sel, XCB_CURRENT_TIME);
     xcb_flush(remoteVars->selstruct.xcbConnection);
     remoteVars->selstruct.inSelection[selection].owner=TRUE;
-    remoteVars->selstruct.inSelection[selection].timestamp=XCB_CURRENT_TIME;
+    remoteVars->selstruct.inSelection[selection].timestamp=currentTime.milliseconds;
+
+//     EPHYR_DBG("own selection %d", currentTime.milliseconds);
     return 0;
 }
+
 void selection_init(struct _remoteHostVars *obj)
 {
     remoteVars=obj;
@@ -82,24 +353,23 @@ void selection_init(struct _remoteHostVars *obj)
 }
 
 
-xcb_atom_t atom(const char* name)
+xcb_atom_t string_to_atom(const char* name)
 {
     //get atom for the name, return 0 if not found
     xcb_intern_atom_cookie_t cookie;
     xcb_intern_atom_reply_t *reply;
     xcb_atom_t a=0;
-
     cookie = xcb_intern_atom(remoteVars->selstruct.xcbConnection, 0, strlen(name), name);
     if ((reply = xcb_intern_atom_reply(remoteVars->selstruct.xcbConnection, cookie, NULL)))
     {
         a=reply->atom;
-//         EPHYR_DBG("The %s atom has ID %u", name, a);
         free(reply);
     }
+    EPHYR_DBG("The %s atom has ID %u", name, a);
     return a;
 }
 
-char *atom_name(xcb_atom_t xatom)
+char *atom_to_string(xcb_atom_t xatom)
 {
     //get name for atom, don't forget to free return value
     char* name;
@@ -120,13 +390,10 @@ char *atom_name(xcb_atom_t xatom)
     return name;
 }
 
-static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, const char *name)
+static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, xcb_atom_t a)
 {
     //check if the atom which represents "name" is in the list of supported mime types
     size_t i = 0;
-    xcb_atom_t a=atom(name);
-    if(!a)
-        return 0;
 
     for (i = 0;i < size;i++)
     {
@@ -136,31 +403,24 @@ static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, const char *nam
     return 0;
 }
 
-static int is_string_atom( xcb_atom_t at)
+int is_string_atom( xcb_atom_t at)
 {
     //check if selection data is string/text
-    if(!at)
-        return 0;
-    if( at == atom("UTF8_STRING") ||
-        at == atom("text/plain;charset=utf-8") ||
-        at == atom("STRING") ||
-        at == atom("TEXT") ||
-        at == atom("text/plain"))
-        return 1;
+    if((at==ATOM_UTF8_STRING) || (at==ATOM_TEXT_PLAIN_UTF) ||
+        (at==ATOM_STRING) || (at==ATOM_TEXT) || (at==ATOM_TEXT_PLAIN))
+            return 1;
     return 0;
 }
 
-static int is_image_atom( xcb_atom_t at)
+int is_image_atom( xcb_atom_t at)
 {
     //check if selection data is image
-    if(!at)
-        return 0;
-    if( at == atom("image/png") ||
-        at == atom("image/xpm") ||
-        at == atom("image/jpg") ||
-        at == atom("image/jpeg") ||
-        at == atom("PIXMAP") ||
-        at == atom("image/bmp"))
+    if( at == ATOM_IMAGE_PNG ||
+        at == ATOM_IMAGE_XPM ||
+        at == ATOM_IMAGE_JPEG ||
+        at == ATOM_IMAGE_JPG ||
+        at == ATOM_PIXMAP ||
+        at == ATOM_IMAGE_BMP)
         return 1;
     return 0;
 }
@@ -170,59 +430,59 @@ static xcb_atom_t best_atom_from_target(xcb_atom_t* list, size_t size)
     //here we chose the best of supported formats for selection
     xcb_atom_t a;
     //selecting utf formats first
-    if((a=target_has_atom(list, size, "UTF8_STRING")))
+    if((a=target_has_atom(list, size, ATOM_UTF8_STRING)))
     {
 //         EPHYR_DBG("selecting mime type UTF8_STRING");
         return a;
     }
-    if((a=target_has_atom(list, size, "text/plain;charset=utf-8")))
+    if((a=target_has_atom(list, size, ATOM_TEXT_PLAIN_UTF)))
     {
 //         EPHYR_DBG("selecting mime type text/plain;charset=utf-8");
         return a;
     }
-    if((a=target_has_atom(list, size, "STRING")))
+    if((a=target_has_atom(list, size, ATOM_STRING)))
     {
 //         EPHYR_DBG( "selecting mime type STRING");
         return a;
     }
-    if((a=target_has_atom(list, size, "TEXT")))
+    if((a=target_has_atom(list, size, ATOM_TEXT)))
     {
 //         EPHYR_DBG( "selecting mime type TEXT");
         return a;
     }
-    if((a=target_has_atom(list, size, "text/plain")))
+    if((a=target_has_atom(list, size, ATOM_TEXT_PLAIN)))
     {
 //         EPHYR_DBG( "selecting mime type text/plain");
         return a;
     }
 
     //selecting loseless formats first
-    if((a=target_has_atom(list, size, "image/png")))
+    if((a=target_has_atom(list, size, ATOM_IMAGE_PNG)))
     {
 //         EPHYR_DBG( "selecting mime type image/png");
         return a;
     }
-    if((a=target_has_atom(list, size, "image/xpm")))
+    if((a=target_has_atom(list, size, ATOM_IMAGE_XPM)))
     {
 //         EPHYR_DBG( "selecting mime type image/xpm");
         return a;
     }
-    if((a=target_has_atom(list, size, "PIXMAP")))
+    if((a=target_has_atom(list, size, ATOM_PIXMAP)))
     {
 //         EPHYR_DBG( "selecting mime type PIXMAP");
         return a;
     }
-    if((a=target_has_atom(list, size, "image/bmp")))
+    if((a=target_has_atom(list, size, ATOM_IMAGE_BMP)))
     {
 //         EPHYR_DBG( "selecting mime type image/bmp");
         return a;
     }
-    if((a=target_has_atom(list, size, "image/jpg")))
+    if((a=target_has_atom(list, size, ATOM_IMAGE_JPG)))
     {
 //         EPHYR_DBG( "selecting mime type image/jpg");
         return a;
     }
-    if((a=target_has_atom(list, size, "image/jpeg")))
+    if((a=target_has_atom(list, size, ATOM_IMAGE_JPEG)))
     {
 //         EPHYR_DBG( "selecting mime type image/jpeg");
         return a;
@@ -245,13 +505,13 @@ void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t
 
 void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
 {
-    xcb_atom_t* tg;
-    char* stype, *sprop;
     xcb_atom_t data_atom;
     unsigned int bytes_left, bytes_read=0;
     xcb_get_property_cookie_t cookie;
     xcb_get_property_reply_t *reply;
-    outputChunk* chunk;
+    struct OutputChunk* chunk;
+    unsigned char* compressed_data;
+    uint32_t compressed_size;
 
 
     //request property which represents value of selection (data or mime types)
@@ -282,7 +542,7 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                 free(sprop);
 */
             //need to read property incrementally
-            if(reply->type == atom("INCR"))
+            if(reply->type == ATOM_INCR)
             {
                 unsigned int sz=*((unsigned int*) xcb_get_property_value(reply));
 //                 EPHYR_DBG( "have incr property size: %d", sz);
@@ -297,7 +557,7 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                 return;
             }
             //we have supported mime types in reply
-            if(reply->type == atom( "ATOM"))
+            if(reply->type == ATOM_ATOM)
             {
                 if(reply->format!=32)
                 {
@@ -328,7 +588,29 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                     xcb_flush(remoteVars->selstruct.xcbConnection);
 
                     if(data_atom)
-                        request_selection_data( selection, data_atom, data_atom, 0);
+                    {
+                        if(remoteVars->client_os == OS_WINDOWS && selection_from_atom(selection) == PRIMARY)
+                        {
+//                             EPHYR_DBG("client doesn't support PRIMARY selection");
+                        }
+                        else
+                        {
+                            if(remoteVars->selstruct.clientSupportsOnDemandSelection)
+                            {
+                                //don't ask for data yet, only send notification that we have a selection to client
+//                                 EPHYR_DBG("client supports onDemand selection, notify client");
+                                send_notify_to_client(selection, data_atom);
+                                //save the data atom for possible data demand
+                                remoteVars->selstruct.best_atom[selection_from_atom(selection)]=data_atom;
+                            }
+                            else
+                            {
+                                //request selection data
+//                                 EPHYR_DBG("client not supports onDemand selection, request data");
+                                request_selection_data( selection, data_atom, data_atom, 0);
+                            }
+                        }
+                    }
                     else
                     {
                         EPHYR_DBG( "there are no supported mime types in the target");
@@ -351,9 +633,9 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                         fclose(cp);*/
 
 
-                        chunk=(outputChunk*) malloc(sizeof(outputChunk));
+                        chunk=malloc(sizeof(struct OutputChunk));
 
-                        memset((void*)chunk,0,sizeof(outputChunk));
+                        memset((void*)chunk,0,sizeof(struct OutputChunk));
 
                         if(xcb_get_property_value_length(reply))
                         {
@@ -362,9 +644,23 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                             memcpy(chunk->data, xcb_get_property_value(reply),chunk->size);
                         }
 
-                        chunk->compressed=FALSE;
+                        chunk->compressed_size=0;
                         if(is_string_atom(property))
+                        {
                             chunk->mimeData=UTF_STRING;
+                            //for text chunks > 1K using zlib compression if client supports it
+                            if(remoteVars->selstruct.clientSupportsExetndedSelection && chunk->size > 1024)
+                            {
+                                compressed_data=zcompress(chunk->data, chunk->size, &compressed_size);
+                                if(compressed_data && compressed_size)
+                                {
+                                    free(chunk->data);
+                                    chunk->data=compressed_data;
+                                    chunk->compressed_size=compressed_size;
+//                                     EPHYR_DBG("compressed chunk from %d to %d", chunk->size, chunk->compressed_size);
+                                }
+                            }
+                        }
                         else
                             chunk->mimeData=PIXMAP;
 
@@ -410,7 +706,8 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                         }
 
                         bytes_read+=xcb_get_property_value_length(reply);
-//                         EPHYR_DBG( "read chunk of selection - size %d, total read %d,  left %d, first:%d, last:%d", xcb_get_property_value_length(reply), bytes_read, bytes_left, chunk->firstChunk, chunk->lastChunk);
+//                          EPHYR_DBG( "read chunk of selection - size %d, total read %d,  left %d, first:%d, last:%d", xcb_get_property_value_length(reply), bytes_read, bytes_left, chunk->firstChunk, chunk->lastChunk);
+
 
                         pthread_mutex_lock(&remoteVars->sendqueue_mutex);
                         //attach chunk to the end of output chunk queue
@@ -420,11 +717,13 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                         }
                         else
                         {
-                            remoteVars->selstruct.lastOutputChunk->next=(struct outputChunk*)chunk;
+                            remoteVars->selstruct.lastOutputChunk->next=chunk;
                             remoteVars->selstruct.lastOutputChunk=chunk;
                         }
 //                         EPHYR_DBG(" ADD CHUNK %p %p %p", remoteVars->selstruct.firstOutputChunk, remoteVars->selstruct.lastOutputChunk, chunk);
                         pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+
+
                         pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
 
                         if(bytes_left)
@@ -444,10 +743,7 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                 }
                 else
                 {
-                    stype=atom_name(reply->type);
-                    EPHYR_DBG("Not supported mime type: %s, %d",stype, reply->type);
-                    if(stype)
-                        free(stype);
+                    EPHYR_DBG("Not supported mime type:%d",reply->type);
                 }
             }
             if(reply)
@@ -459,12 +755,65 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
     }
 }
 
+void send_notify_to_client(xcb_atom_t selection, xcb_atom_t mime)
+{
+    //creating the selection chunk with no data, which notifyes client that we have a selection
+    struct OutputChunk* chunk= malloc(sizeof(struct OutputChunk));
+//     EPHYR_DBG("send selection notify to client");
+
+
+    memset((void*)chunk,0,sizeof(struct OutputChunk));
+
+    if(is_string_atom(mime))
+        chunk->mimeData=UTF_STRING;
+    else
+        chunk->mimeData=PIXMAP;
+
+    chunk->selection=selection_from_atom(selection);
+    chunk->totalSize=0;
+    chunk->firstChunk=chunk->lastChunk=TRUE;
+
+    pthread_mutex_lock(&remoteVars->sendqueue_mutex);
+
+    //attach chunk to the end of output chunk queue
+    if(!remoteVars->selstruct.lastOutputChunk)
+    {
+        remoteVars->selstruct.lastOutputChunk=remoteVars->selstruct.firstOutputChunk=chunk;
+    }
+    else
+    {
+        remoteVars->selstruct.lastOutputChunk->next=chunk;
+        remoteVars->selstruct.lastOutputChunk=chunk;
+    }
+    pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+
+
+    pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
+}
+
 void process_selection_notify(xcb_generic_event_t *e)
 {
     xcb_selection_notify_event_t *sel_event;
 
+    enum SelectionType selection;
+
 //     EPHYR_DBG("selection notify");
     sel_event=(xcb_selection_notify_event_t *)e;
+    selection=selection_from_atom(sel_event->selection);
+
+    if(sel_event->property== XCB_NONE && sel_event->target==XCB_NONE)
+    {
+        //have data demand from client
+        request_selection_data( sel_event->selection, remoteVars->selstruct.best_atom[selection],remoteVars->selstruct.best_atom[selection], 0);
+        return;
+    }
+
+    if(sel_event->property== sel_event->selection && sel_event->target==sel_event->selection)
+    {
+        //have data ready from server. We don't need to do anything here. This event interrrupted the waiting procedure and the delayed requests are already processed
+//         EPHYR_DBG("Have DATA READY event");
+        return;
+    }
 
     //processing the event which is reply for convert selection call
 
@@ -481,7 +830,7 @@ void process_selection_notify(xcb_generic_event_t *e)
 //         EPHYR_DBG("selection notify sel %d, target %d, property %d", sel_event->selection, sel_event->target, sel_event->property);
         if(sel_event->property==XCB_NONE)
         {
-            EPHYR_DBG( "NO SELECTION");
+//             EPHYR_DBG( "NO SELECTION");
         }
         else
         {
@@ -501,7 +850,9 @@ void process_property_notify(xcb_generic_event_t *e)
     pn = (xcb_property_notify_event_t *)e;
     if (pn->window != remoteVars->selstruct.clipWinId)
     {
-//         EPHYR_DBG("not our window");
+        //this property doesn't belong to our window;
+        //let's check if it's not the property corresponding to one of incr transactions
+        process_incr_transaction_property(pn);
         return;
     }
 //     EPHYR_DBG("property %d, state %d ", pn->atom, pn->state);
@@ -552,10 +903,15 @@ void process_selection_owner_notify(xcb_generic_event_t *e)
     selection=selection_from_atom(notify_event->selection);
 
     //we are not owners of this selction anymore
+
+    pthread_mutex_lock(&remoteVars->selstruct.inMutex);
     remoteVars->selstruct.inSelection[selection].owner=FALSE;
 
+
+    pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+
     //get supported mime types
-    request_selection_data( notify_event->selection, atom( "TARGETS"), atom( "TARGETS"), 0);
+    request_selection_data( notify_event->selection, ATOM_TARGETS, ATOM_TARGETS, 0);
 
 }
 
@@ -588,7 +944,7 @@ void *selection_thread (void* id)
     values[0] = screen->white_pixel;
     values[1] = XCB_EVENT_MASK_PROPERTY_CHANGE;
 
-    ATOM_CLIPBOARD=atom("CLIPBOARD");
+    init_atoms();
 
     //create window which will recieve selection events and provide remote selection to X-clients
     xcb_create_window (remoteVars->selstruct.xcbConnection,
@@ -627,6 +983,8 @@ void *selection_thread (void* id)
     // event loop
     while ((e = xcb_wait_for_event(remoteVars->selstruct.xcbConnection)))
     {
+        process_delayed_requests();
+        remove_obsolete_incr_transactions(TRUE);
         response_type = e->response_type & ~0x80;
 
         //we notified that selection is changed in primary or clipboard
@@ -648,7 +1006,11 @@ void *selection_thread (void* id)
             }
             else if (response_type == XCB_SELECTION_REQUEST)
             {
-                process_selection_request(e);
+                if(!process_selection_request(e))
+                {
+                    //we delayed this request, not deleteing the event yet
+                    continue;
+                }
             }
             else
             {
@@ -682,10 +1044,53 @@ void install_selection_callbacks(void)
     return;
 }
 
-void process_selection_request(xcb_generic_event_t *e)
+void client_sel_request_notify(enum SelectionType sel)
 {
+    //this function will be used from main thread to send the event which will
+    // notify selection thread that client want us to send data for selection sel
+
+    xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
+    event->response_type = XCB_SELECTION_NOTIFY;
+    event->requestor = remoteVars->selstruct.clipWinId;
+    event->selection = atom_from_selection(sel);
+    event->target    = XCB_NONE;
+    event->property  = XCB_NONE;
+    event->time      = XCB_TIME_CURRENT_TIME;
+
+    xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, remoteVars->selstruct.clipWinId, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+    free(event);
+}
+
+void client_sel_data_notify(enum SelectionType sel)
+{
+    //this function will be used from main thread to send the event which will
+    // notify selection thread that client sent us data for selection sel
+
+    xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
+    event->response_type = XCB_SELECTION_NOTIFY;
+    event->requestor = remoteVars->selstruct.clipWinId;
+    event->selection = atom_from_selection(sel);
+    event->target    = atom_from_selection(sel);
+    event->property  = atom_from_selection(sel);
+    event->time      = XCB_TIME_CURRENT_TIME;
+
+    xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, remoteVars->selstruct.clipWinId, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+    free(event);
+}
+
+
+BOOL process_selection_request(xcb_generic_event_t *e)
+{
+    //processing selection request.
+    //return true if the processing is finishing after return
+    //false if data is not ready and we are delaying processing of this request
+    //in this case calling function SHOULD NOT destroy the request neither event should not be destroyed
+    //we'll free this objects after processing of the request when the data is available
+
     xcb_selection_request_event_t *req=(xcb_selection_request_event_t*)e;
-    char *asel, *atar, *aprop;
+
     enum SelectionType sel=selection_from_atom(req->selection);
     xcb_atom_t property=req->property;
     xcb_atom_t target=req->target;
@@ -702,45 +1107,35 @@ void process_selection_request(xcb_generic_event_t *e)
     if(property == XCB_NONE)
         property=target;
 
-
-/*
-    asel= atom_name(req->selection);
-    atar= atom_name(req->target);
-    aprop=atom_name(req->property);
-
-    EPHYR_DBG("selection request for %s %s %s ", asel, atar, aprop);
-    if(asel)
-        free(asel);
-    if(aprop)
-        free(aprop);
-    if(atar)
-        free(atar);
-*/
-
     //synchronize with main thread
+
     pthread_mutex_lock(&remoteVars->selstruct.inMutex);
     if(! remoteVars->selstruct.inSelection[sel].owner)
     {
+
+
         pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
         //we don't own this selection
-        EPHYR_DBG("not our selection");
+//         EPHYR_DBG("not our selection");
         xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
         xcb_flush(remoteVars->selstruct.xcbConnection);
         free(event);
-        return;
+        return TRUE;
     }
 
     if(remoteVars->selstruct.inSelection[sel].timestamp > req->time)
     {
+
+
         pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
         //selection changed after request
-        EPHYR_DBG("requested selection doesn't exist anymore");
+//         EPHYR_DBG("requested selection doesn't exist anymore");
         xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
         xcb_flush(remoteVars->selstruct.xcbConnection);
         free(event);
-        return;
+        return TRUE;
     }
-    if(req->target==atom("TIMESTAMP"))
+    if(req->target==ATOM_TIMESTAMP)
     {
         event->property=property;
 //         EPHYR_DBG("requested TIMESTAMP");
@@ -748,7 +1143,7 @@ void process_selection_request(xcb_generic_event_t *e)
                             property, XCB_ATOM_INTEGER, 32, 1, &remoteVars->selstruct.inSelection[sel].timestamp);
 
     }
-    else if(req->target==atom("TARGETS"))
+    else if(req->target==ATOM_TARGETS)
     {
         event->property=property;
 //         EPHYR_DBG("requested TARGETS");
@@ -756,12 +1151,32 @@ void process_selection_request(xcb_generic_event_t *e)
     }
     else
     {
-        event->property=send_data(req);
+        if(remoteVars->selstruct.inSelection[sel].state==COMPLETED)
+            event->property=send_data(req);
+        else
+        {
+//             EPHYR_DBG("the data for %d is not ready yet",sel);
+            delay_selection_request(req,event);
+
+
+            pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+            return FALSE;
+        }
     }
+
+
     pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
     xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
     xcb_flush(remoteVars->selstruct.xcbConnection);
     free(event);
+    return TRUE;
+}
+
+xcb_atom_t atom_from_selection(enum SelectionType sel)
+{
+    if(sel==PRIMARY)
+        return XCB_ATOM_PRIMARY;
+    return ATOM_CLIPBOARD;
 }
 
 enum SelectionType selection_from_atom(xcb_atom_t selection)
@@ -780,42 +1195,42 @@ void send_mime_types(xcb_selection_request_event_t* req)
 
     //we'll have max 7 mimetypes, don't forget to change this dimension if adding new mimetypes
     xcb_atom_t targets[7];
-    xcb_atom_t a;
     uint32_t mcount=0;
 
-    if((a=atom("TARGETS")))
-        targets[mcount++]=a;
-    if((a=atom("TIMESTAMP")))
-        targets[mcount++]=a;
+    targets[mcount++]=ATOM_TARGETS;
+    targets[mcount++]=ATOM_TIMESTAMP;
 
     if(remoteVars->selstruct.inSelection[sel].mimeData==PIXMAP)
     {
+
+        //only supporting PNG here at the moment
+        targets[mcount++]=ATOM_IMAGE_PNG;
+
+/*
         //return PNG mime, if our data in PNG format, otherwise return JPEG
         if((a=atom("image/png")) && is_png(remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size))
         {
             //our buffer is PNG file
             targets[mcount++]=a;
+            EPHYR_DBG("SENDING PNG ATOMS");
         }
         else
         {
+            EPHYR_DBG("SENDING JPG ATOMS");
             if((a=atom("image/jpg")))
                 targets[mcount++]=a;
             if((a=atom("image/jpeg")))
                 targets[mcount++]=a;
-        }
+        }*/
     }
     else
     {
-        if((a=atom("UTF8_STRING")))
-            targets[mcount++]=a;
-        if((a=atom("text/plain;charset=utf-8")))
-            targets[mcount++]=a;
-        if((a=atom("STRING")))
-            targets[mcount++]=a;
-        if((a=atom("TEXT")))
-            targets[mcount++]=a;
-        if((a=atom("text/plain")))
-            targets[mcount++]=a;
+//         EPHYR_DBG("SENDING STRING ATOMS");
+        targets[mcount++]=ATOM_UTF8_STRING;
+        targets[mcount++]=ATOM_TEXT_PLAIN_UTF;
+        targets[mcount++]=ATOM_STRING;
+        targets[mcount++]=ATOM_TEXT;
+        targets[mcount++]=ATOM_TEXT_PLAIN;
     }
 
     xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, XCB_ATOM_ATOM,
@@ -828,11 +1243,7 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
     //inmutex is locked in the caller function
     //send data
     enum SelectionType sel=selection_from_atom(req->selection);
-    char *starget;
 
-    xcb_atom_t png=atom("image/png");
-    xcb_atom_t jpg=atom("image/jpg");
-    xcb_atom_t jpeg=atom("image/jpeg");
 
     if(remoteVars->selstruct.inSelection[sel].mimeData==UTF_STRING)
     {
@@ -844,10 +1255,7 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
         }
         else
         {
-            starget=atom_name(req->target);
-            EPHYR_DBG("unsupported property requested: %s",starget);
-            if(starget)
-                free(starget);
+            EPHYR_DBG("unsupported property requested: %d",req->target);
             return XCB_NONE;
         }
     }
@@ -855,10 +1263,7 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
     {
         if(!is_image_atom(req->target))
         {
-            starget=atom_name(req->target);
-            EPHYR_DBG("unsupported property requested: %s",starget);
-            if(starget)
-                free(starget);
+            EPHYR_DBG("unsupported property requested: %d",req->target);
             return XCB_NONE;
         }
 /*
@@ -871,23 +1276,17 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
         //TODO: implement convertion between different image formats
         if(is_png(remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size))
         {
-            if(req->target!=png)
+            if(req->target!=ATOM_IMAGE_PNG)
             {
-                starget=atom_name(req->target);
-                EPHYR_DBG("unsupported property requested: %s",starget);
-                if(starget)
-                    free(starget);
+                EPHYR_DBG("unsupported property requested: %d",req->target);
                 return XCB_NONE;
             }
         }
         else
         {
-            if((req->target!=jpg)&&(req->target!=jpeg))
+            if((req->target!=ATOM_IMAGE_JPEG)&&(req->target!=ATOM_IMAGE_JPG))
             {
-                starget=atom_name(req->target);
-                EPHYR_DBG("unsupported property requested: %s",starget);
-                if(starget)
-                    free(starget);
+                EPHYR_DBG("unsupported property requested: %d",req->target);
                 return XCB_NONE;
             }
         }
@@ -902,36 +1301,166 @@ xcb_atom_t set_data_property(xcb_selection_request_event_t* req, unsigned char*
     //set data to window property
 
     //change when implemented
-    BOOL support_incr=FALSE;
+    BOOL support_incr=TRUE;
 
-    xcb_atom_t xtsel=atom("_XT_SELECTION_0");
-    xcb_atom_t qtsel=atom("_QT_SELECTION");
     //this types of application not supporting incr selection
-    if(req->property == xtsel|| req->property == qtsel )
+    if(req->property == ATOM_XT_SELECTION|| req->property == ATOM_QT_SELECTION )
     {
         EPHYR_DBG("property %d doesn't support INCR",req->property);
         support_incr=FALSE;
     }
 
     //check if we are sending incr
-    if(size < xcb_get_maximum_request_length(remoteVars->selstruct.xcbConnection) * 4 - 24)
+    if(!support_incr)
     {
-//         EPHYR_DBG( "sending %d bytes, property %d, target %d", size, req->property, req->target);
+        if(size < xcb_get_maximum_request_length(remoteVars->selstruct.xcbConnection) * 4 - 24)
+        {
+            //         EPHYR_DBG( "sending %d bytes, property %d, target %d", size, req->property, req->target);
 
+            xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
+                                8, size, (const void *)data);
+
+            xcb_flush(remoteVars->selstruct.xcbConnection);
+            return req->property;
+        }
+        //the data is to big to sent in one property and requestor doesn't support INCR
+        EPHYR_DBG("data is too big");
+        return XCB_NONE;
+    }
+    if(size < INCR_SIZE)
+    {
+        //if size is < 256K send in one property
         xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
                             8, size, (const void *)data);
 
         xcb_flush(remoteVars->selstruct.xcbConnection);
         return req->property;
     }
+    //sending INCR atom to let requester know that we are starting data incrementally
+//     EPHYR_DBG("starting INCR send of size %d  for win ID %d" ,size, req->requestor);
+    xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property,
+                        ATOM_INCR, 32, 1, (const void *)&size);
 
-    EPHYR_DBG("data is too big");
-    return XCB_NONE;
+    start_incr_transaction(req->requestor, req->property, req->target, data, size);
+
+
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+    return req->property;
 }
 
+
+void start_incr_transaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, unsigned char* data, uint32_t size)
+{
+    //creating INCR transaction
+    //inmutex is locked from parent thread
+
+    const uint32_t mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+
+    struct IncrTransaction* tr=malloc( sizeof(struct IncrTransaction));
+    tr->requestor=requestor;
+    tr->property=property;
+    tr->target=target;
+    tr->sentBytes=0;
+    tr->timestamp=currentTime.milliseconds;
+    tr->data=malloc(size);
+    tr->size=size;
+    tr->next=NULL;
+    memcpy(tr->data, data, size);
+
+    //add new transaction to the list
+    if(!remoteVars->selstruct.firstIncrTransaction)
+    {
+        remoteVars->selstruct.firstIncrTransaction=remoteVars->selstruct.lastIncrTransaction=tr;
+    }
+    else
+    {
+        remoteVars->selstruct.lastIncrTransaction->next=tr;
+        remoteVars->selstruct.lastIncrTransaction=tr;
+    }
+
+
+    //we'll recive property change events for requestor window from now
+    xcb_change_window_attributes(remoteVars->selstruct.xcbConnection, requestor,
+                                 XCB_CW_EVENT_MASK, mask);
+}
+
+
 BOOL is_png(unsigned char* data, uint32_t size)
 {
     if( size<8)
         return FALSE;
     return !png_sig_cmp(data, 0, 8);
 }
+
+unsigned char* zcompress(unsigned char *inbuf, uint32_t size, uint32_t* compress_size)
+{
+    //compressing the data with zlib
+    //return compressed data, storing the size of compressed data in compress_size
+    //caller function should chek result of compression and free the output buffer
+
+
+    //out buffer at least the size of input buffer
+    unsigned char* out=malloc(size);
+
+    z_stream stream;
+    stream.zalloc = Z_NULL;
+    stream.zfree = Z_NULL;
+    stream.opaque = Z_NULL;
+
+    stream.avail_in = size;
+    stream.next_in = inbuf;
+    stream.avail_out = size;
+    stream.next_out = out;
+
+    deflateInit(&stream, Z_BEST_COMPRESSION);
+    deflate(&stream, Z_FINISH);
+    deflateEnd(&stream);
+
+    if(!stream.total_out || stream.total_out >= size)
+    {
+        EPHYR_DBG("zlib compression failed");
+        free(out);
+        *compress_size=0;
+        return NULL;
+    }
+    *compress_size=stream.total_out;
+    return out;
+}
+
+void delay_selection_request( xcb_selection_request_event_t *request, xcb_selection_notify_event_t* event)
+{
+    //delay the request for later processing when data will be ready
+    //inmutex is locked in the caller function
+    enum SelectionType sel=selection_from_atom(request->selection);
+    struct DelayedRequest* dr = malloc( sizeof(struct DelayedRequest));
+    dr->event=event;
+    dr->request=request;
+    dr->next=NULL;
+
+    //add new request to the queue
+    if(!remoteVars->selstruct.firstDelayedRequest)
+    {
+        remoteVars->selstruct.firstDelayedRequest=remoteVars->selstruct.lastDelayedRequest=dr;
+    }
+    else
+    {
+        remoteVars->selstruct.lastDelayedRequest->next=dr;
+        remoteVars->selstruct.lastDelayedRequest=dr;
+    }
+
+    if(remoteVars->selstruct.inSelection[sel].state==NOTIFIED)
+    {
+        //if we didn't request the data yet, let's do it now
+//         EPHYR_DBG("requesting data");
+
+        pthread_mutex_lock(&remoteVars->sendqueue_mutex);
+        remoteVars->selstruct.requestSelection[sel] = TRUE;
+        pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+
+
+        pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
+        remoteVars->selstruct.inSelection[sel].state=REQUESTED;
+
+    }
+}
+
diff --git a/x2gokdriveselection.h b/x2gokdriveselection.h
index 7ec8d8a..e144d20 100644
--- a/x2gokdriveselection.h
+++ b/x2gokdriveselection.h
@@ -36,18 +36,37 @@ void selection_init(struct _remoteHostVars *obj);
 void install_selection_callbacks(void);
 int own_selection(enum SelectionType selection);
 
-xcb_atom_t atom(const char* name);
-char *atom_name(xcb_atom_t xatom);
+int is_string_atom( xcb_atom_t at);
+int is_image_atom( xcb_atom_t at);
+
+xcb_atom_t string_to_atom(const char* name);
+char *atom_to_string(xcb_atom_t xatom);
+void init_atoms(void);
+
+void send_notify_to_client(xcb_atom_t selection, xcb_atom_t mime);
 void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t);
 void read_selection_property(xcb_atom_t selection, xcb_atom_t property);
 void process_selection_notify(xcb_generic_event_t *e);
 void process_property_notify(xcb_generic_event_t *e);
 void process_selection_owner_notify(xcb_generic_event_t *e);
-void process_selection_request(xcb_generic_event_t *e);
+BOOL process_selection_request(xcb_generic_event_t *e);
 void send_mime_types(xcb_selection_request_event_t* req);
 enum SelectionType selection_from_atom(xcb_atom_t selection);
+xcb_atom_t atom_from_selection(enum SelectionType sel);
 xcb_atom_t send_data(xcb_selection_request_event_t* req);
 xcb_atom_t set_data_property(xcb_selection_request_event_t* req, unsigned char* data, uint32_t size);
+void client_sel_request_notify(enum SelectionType sel);
+void client_sel_data_notify(enum SelectionType sel);
 BOOL is_png(unsigned char* data, uint32_t size);
+void delay_selection_request( xcb_selection_request_event_t *reqest, xcb_selection_notify_event_t* event);
+void process_delayed_requests(void);
+struct DelayedRequest* discard_delayed_request(struct DelayedRequest* d, struct DelayedRequest* prev);
+BOOL check_req_sanity(xcb_selection_request_event_t* req);
+void start_incr_transaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, unsigned char* data, uint32_t size);
+void process_incr_transaction_property(xcb_property_notify_event_t * pn);
+void destroy_incr_transaction(struct IncrTransaction* tr, struct IncrTransaction* prev);
+void remove_obsolete_incr_transactions( BOOL checkTs);
+
+unsigned char* zcompress(unsigned char *inbuf, uint32_t size, uint32_t* compress_size);
 
 #endif /* X2GOKDRIVESELECTION_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