[X2Go-Commits] [x2gokdrive] 01/01: Move selection functionality to separate thread. Use XCB API to manage selections.

git-admin at x2go.org git-admin at x2go.org
Thu Aug 20 22:45:12 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 3d6295b9f0163e87df733a2490bbbe7d81b66f6d
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date:   Thu Aug 20 15:45:02 2020 -0500

    Move selection functionality to separate thread. Use XCB API to manage selections.
---
 debian/changelog      |    2 +
 x2gokdriveinit.c      |    2 +
 x2gokdriveremote.c    |  362 ++++++++------
 x2gokdriveremote.h    |   75 +--
 x2gokdriveselection.c | 1252 +++++++++++++++++++++++++++++--------------------
 x2gokdriveselection.h |   18 +-
 6 files changed, 1052 insertions(+), 659 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 3ca47a9..152b81b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -9,6 +9,8 @@ x2gokdrive (0.0.0.1-0x2go1) UNRELEASED; urgency=medium
   * Calculate screen dimensions (in mm) according to dpi value.
     Default DPI (Xorg) is 75. User can set DPI using -dpi command
     line option.
+    - Move selection functionality to separate thread. Use XCB API to manage
+      selections.
 
   [ Mihai Moldovan ]
   * Initial release:
diff --git a/x2gokdriveinit.c b/x2gokdriveinit.c
index bfad59f..f37e90b 100644
--- a/x2gokdriveinit.c
+++ b/x2gokdriveinit.c
@@ -256,6 +256,8 @@ ddxProcessArgument(int argc, char **argv, int i)
         /* compat with nxagent */
         return 1;
     }
+    else if (argv[i][0] == ':')
+        remote_set_display_name(argv[i]);
 
     return KdProcessArgument(argc, argv, i);
 }
diff --git a/x2gokdriveremote.c b/x2gokdriveremote.c
index ac524f0..7732deb 100644
--- a/x2gokdriveremote.c
+++ b/x2gokdriveremote.c
@@ -528,17 +528,44 @@ int send_deleted_cursors(void)
     return sent;
 }
 
-int send_selection(int sel, char* data, uint32_t length, uint32_t format)
+int send_output_selection(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);
+    }
+    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)
+        {
+            return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, TRUE, TRUE, FALSE, chunk->size);
+        }
+        EPHYR_DBG("Client doesn't support extended selection, not sending this 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)
 {
     unsigned char buffer[56] = {0};
     _X_UNUSED int ln = 0;
     int l = 0;
     int sent = 0;
 
-    *((uint32_t*)buffer)=SELECTION;
-    *((uint32_t*)buffer+1)=sel;
-    *((uint32_t*)buffer+2)=format;
-    *((uint32_t*)buffer+3)=length;
+
+    *((uint32_t*)buffer)=SELECTION;    //0
+    *((uint32_t*)buffer+1)=sel;        //4
+    *((uint32_t*)buffer+2)=format;     //8
+    *((uint32_t*)buffer+3)=length;     //16
+    *((uint32_t*)buffer+4)=first;      //20
+    *((uint32_t*)buffer+5)=last;       //24
+    *((uint32_t*)buffer+6)=compressed; //28
+    *((uint32_t*)buffer+7)=total; //28
+
 
 //    #warning check this
     ln=write(remoteVars.clientsock,buffer,56);
@@ -1470,7 +1497,7 @@ void *send_frame_thread (void *threadid)
             remoteVars.client_connected=TRUE;
 
 
-            if(!remoteVars.first_sendqueue_element && !remoteVars.firstCursor)
+            if(!remoteVars.first_sendqueue_element && !remoteVars.firstCursor && !remoteVars.selstruct.firstOutputChunk)
             {
                 /* sleep if frame queue is empty */
                 pthread_cond_wait(&remoteVars.have_sendqueue_cond, &remoteVars.sendqueue_mutex);
@@ -1478,6 +1505,30 @@ void *send_frame_thread (void *threadid)
 
             /* mutex is locked on this point */
 
+            //only send output selection chunks if there are no frames and cursors in the queue
+            //selections can take a lot of bandwidth and have less priority
+            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;
+                if(!remoteVars.selstruct.firstOutputChunk)
+                {
+                    remoteVars.selstruct.lastOutputChunk=NULL;
+                }
+                pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+                //send chunk
+                send_output_selection(chunk);
+                //free chunk and it's data
+                if(chunk->data)
+                {
+                      free(chunk->data);
+                }
+//                 EPHYR_DBG(" REMOVE CHUNK %p %p %p", remoteVars.selstruct.firstOutputChunk, remoteVars.selstruct.lastOutputChunk, chunk);
+                free(chunk);
+                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 */
@@ -1499,40 +1550,6 @@ void *send_frame_thread (void *threadid)
                 pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
             }
 
-            pthread_mutex_lock(&remoteVars.sendqueue_mutex);
-            if(remoteVars.selstruct.clipboard.changed)
-            {
-                size_t sz=remoteVars.selstruct.clipboard.size;
-                char* data=malloc(sz);
-                int format = 0;
-
-                memcpy(data, remoteVars.selstruct.clipboard.data, sz);
-                remoteVars.selstruct.clipboard.changed=FALSE;
-                format=remoteVars.selstruct.clipboard.mimeData;
-                pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
-                send_selection(CLIPBOARD,data,sz, format);
-                free(data);
-            }
-            else
-                pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
-
-            pthread_mutex_lock(&remoteVars.sendqueue_mutex);
-            if(remoteVars.selstruct.selection.changed)
-            {
-                size_t sz=remoteVars.selstruct.selection.size;
-                char* data=malloc(sz);
-                int format = 0;
-
-                memcpy(data, remoteVars.selstruct.selection.data, sz);
-                remoteVars.selstruct.selection.changed=FALSE;
-                format=remoteVars.selstruct.selection.mimeData;
-                pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
-                send_selection(PRIMARY, data, sz, format);
-                free(data);
-            }
-            else
-                pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
-
             pthread_mutex_lock(&remoteVars.sendqueue_mutex);
             if(remoteVars.first_sendqueue_element)
             {
@@ -1637,6 +1654,25 @@ void *send_frame_thread (void *threadid)
     pthread_exit(0);
 }
 
+/* warning! sendqueue_mutex should be locked by thread calling this function! */
+void clear_output_selection(void)
+{
+    outputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+    outputChunk* prev_chunk;
+
+    while(chunk)
+    {
+        prev_chunk=chunk;
+        chunk=(outputChunk*)chunk->next;
+        if(prev_chunk->data)
+        {
+            free(prev_chunk->data);
+        }
+        free(prev_chunk);
+    }
+    remoteVars.selstruct.firstOutputChunk=remoteVars.selstruct.lastOutputChunk=NULL;
+}
+
 /* warning! sendqueue_mutex should be locked by thread calling this function! */
 static
 void clear_send_queue(void)
@@ -1797,10 +1833,132 @@ void disconnect_client(void)
     clear_send_queue();
     clear_frame_cache(0);
     freeCursors();
+    clear_output_selection();
     pthread_cond_signal(&remoteVars.have_sendqueue_cond);
     pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
 }
 
+
+void readInputSelectionBuffer(char* buff)
+{
+    //read th rest of the chunk data
+
+    inputBuffer* selbuff = &remoteVars.selstruct.inSelection[remoteVars.selstruct.readingInputBuffer];
+
+    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;
+
+    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;
+    }
+    //unlock selection
+    pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
+}
+
+void readInputSelectionHeader(char* buff)
+{
+
+    //read the input selection event.
+    //The event represents one chunk of imput selection
+    //it has a header and some data of the chunk
+
+    uint32_t size, totalSize;
+    uint8_t destination, mime;
+    inputBuffer* selbuff = NULL;
+    BOOL firstChunk=FALSE, lastChunk=FALSE;
+    BOOL compressed=FALSE;
+    uint32_t headerSize=10;
+    uint32_t l;
+
+    size=*((uint32_t*)buff+1);
+    destination=*((uint8_t*)buff+8);
+    mime=*((uint8_t*)buff+9);
+
+    //if client supports ext selection, read extended header
+    if(remoteVars.selstruct.clientSupportsExetndedSelection)
+    {
+        headerSize=17;
+        firstChunk=*((uint8_t*)buff + 10);
+        lastChunk=*((uint8_t*)buff + 11);
+        compressed=*((uint8_t*)buff + 12);
+        totalSize=*( (uint32_t*) (buff+13));
+    }
+    else
+    {
+        compressed=FALSE;
+        lastChunk=firstChunk=TRUE;
+        totalSize=size;
+    }
+
+//     EPHYR_DBG("HAVE NEW INCOMING SELECTION Chunk: sel %d size %d mime %d",destination, size, mime);
+
+    //lock selection
+    pthread_mutex_lock(&remoteVars.selstruct.inMutex);
+
+    remoteVars.selstruct.readingInputBuffer=-1;
+
+    selbuff = &remoteVars.selstruct.inSelection[destination];
+    if(firstChunk)
+    {
+        //if it's first chunk, initialize our selection buffer
+        if(selbuff->size && selbuff->data)
+        {
+            free(selbuff->data);
+        }
+        selbuff->size=totalSize;
+        selbuff->mimeData=mime;
+        selbuff->data=malloc(totalSize);
+        selbuff->bytesReady=0;
+    }
+
+    //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(selbuff->size == selbuff->bytesReady)
+    {
+        //Selection is completed
+//         EPHYR_DBG("READY INCOMING SELECTION for %d",destination);
+        //own the selection
+        own_selection(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);
+}
+
 void
 clientReadNotify(int fd, int ready, void *data)
 {
@@ -1839,38 +1997,9 @@ clientReadNotify(int fd, int ready, void *data)
     {
         char* buff=remoteVars.eventBuffer+i*EVLENGTH;
 
-        if(remoteVars.selstruct.readingInputBuffer)
+        if(remoteVars.selstruct.readingInputBuffer != -1)
         {
-            int leftToRead=remoteVars.selstruct.inBuffer.size - remoteVars.selstruct.inBuffer.position;
-            int chunk=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
-
-            memcpy(remoteVars.selstruct.inBuffer.data+remoteVars.selstruct.inBuffer.position, buff, chunk);
-            remoteVars.selstruct.inBuffer.position+=chunk;
-            if(! (remoteVars.selstruct.inBuffer.position < remoteVars.selstruct.inBuffer.size))
-            {
-                inputBuffer* selbuff;
-                if(remoteVars.selstruct.inBuffer.target==PRIMARY)
-                {
-                    selbuff=&remoteVars.selstruct.inSelection;
-                }
-                else
-                {
-                    selbuff=&remoteVars.selstruct.inClipboard;
-                }
-                if(selbuff->data)
-                    free(selbuff->data);
-                selbuff->target=remoteVars.selstruct.inBuffer.target;
-                selbuff->data=remoteVars.selstruct.inBuffer.data;
-                remoteVars.selstruct.inBuffer.data=NULL;
-                selbuff->size=remoteVars.selstruct.inBuffer.size;
-                remoteVars.selstruct.inBuffer.size=0;
-                selbuff->mimeData=remoteVars.selstruct.inBuffer.mimeData;
-                remoteVars.selstruct.readingInputBuffer=FALSE;
-                EPHYR_DBG("READY TARGET %d, MIME %d, Read %d from %d",remoteVars.selstruct.inBuffer.target, selbuff->mimeData,
-                          remoteVars.selstruct.inBuffer.position, selbuff->size);
-                own_selection(selbuff->target);
-            }
-//            EPHYR_DBG("CHUNK IS DONE %d",remoteVars.selstruct.readingInputBuffer);
+            readInputSelectionBuffer(buff);
         }
         else
         {
@@ -2034,47 +2163,7 @@ clientReadNotify(int fd, int ready, void *data)
                 }
                 case SELECTIONEVENT:
                 {
-                    uint32_t size;
-                    uint8_t destination, mime;
-                    inputBuffer* selbuff = NULL;
-
-                    size=*((uint32_t*)buff+1);
-                    destination=*((uint8_t*)buff+8);
-                    mime=*((uint8_t*)buff+9);
-
-                    EPHYR_DBG("HAVE NEW INCOMING SELECTION: %d %d %d",size, destination, mime);
-
-                    if(destination==CLIPBOARD)
-                        selbuff=&(remoteVars.selstruct.inClipboard);
-                    else
-                        selbuff=&(remoteVars.selstruct.inSelection);
-                    if(size < EVLENGTH-10)
-                    {
-                        if(selbuff->data)
-                            free(selbuff->data);
-                        selbuff->size=size;
-                        selbuff->data=malloc(size);
-                        selbuff->target=destination;
-                        memcpy(selbuff->data, buff+10, size);
-                        selbuff->mimeData=mime;
-                        EPHYR_DBG("READY INCOMING SELECTION for %d",destination);
-                        own_selection(selbuff->target);
-
-                    }
-                    else
-                    {
-                        selbuff=&(remoteVars.selstruct.inBuffer);
-                        if(selbuff->data)
-                            free(selbuff->data);
-                        selbuff->data=malloc(size);
-                        memcpy(selbuff->data, buff+10, EVLENGTH-10);
-                        selbuff->mimeData=mime;
-                        selbuff->target=destination;
-                        selbuff->position=EVLENGTH-10;
-                        selbuff->size=size;
-                        remoteVars.selstruct.readingInputBuffer=TRUE;
-                        EPHYR_DBG("READ INCOMING BUFFER %d from %d",EVLENGTH-10, size);
-                    }
+                    readInputSelectionHeader(buff);
                     break;
                 }
                 default:
@@ -2201,6 +2290,15 @@ void terminateServer(int exitStatus)
     {
         pthread_cancel(remoteVars.send_thread_id);
     }
+    if(remoteVars.selstruct.selThreadId)
+    {
+        pthread_cancel(remoteVars.selstruct.selThreadId);
+        if(remoteVars.selstruct.xcbConnection)
+        {
+            xcb_disconnect(remoteVars.selstruct.xcbConnection);
+        }
+        pthread_mutex_destroy(&remoteVars.selstruct.inMutex);
+    }
 
     pthread_mutex_destroy(&remoteVars.mainimg_mutex);
     pthread_mutex_destroy(&remoteVars.sendqueue_mutex);
@@ -2212,27 +2310,14 @@ void terminateServer(int exitStatus)
         free(remoteVars.second_buffer);
     }
 
-    if(remoteVars.selstruct.clipboard.data)
-    {
-        free(remoteVars.selstruct.clipboard.data);
-    }
-
-    if(remoteVars.selstruct.selection.data)
-    {
-        free(remoteVars.selstruct.selection.data);
-    }
 
-    if(remoteVars.selstruct.inClipboard.data)
+    if(remoteVars.selstruct.inSelection[0].data)
     {
-        free(remoteVars.selstruct.inClipboard.data);
+        free(remoteVars.selstruct.inSelection[0].data);
     }
-    if(remoteVars.selstruct.inSelection.data)
+    if(remoteVars.selstruct.inSelection[1].data)
     {
-        free(remoteVars.selstruct.inSelection.data);
-    }
-    if(remoteVars.selstruct.inBuffer.data)
-    {
-        free(remoteVars.selstruct.inBuffer.data);
+        free(remoteVars.selstruct.inSelection[1].data);
     }
     setAgentState(TERMINATED);
     EPHYR_DBG("exit program with status %d", exitStatus);
@@ -2372,6 +2457,7 @@ remote_init(void)
 
     remoteVars.jpegQuality=JPG_QUALITY;
     remoteVars.compression=DEFAULT_COMPRESSION;
+    remoteVars.selstruct.selectionMode = CLIP_BOTH;
 
     pthread_mutex_init(&remoteVars.mainimg_mutex, NULL);
     pthread_mutex_init(&remoteVars.sendqueue_mutex,NULL);
@@ -3085,6 +3171,18 @@ uint32_t calculate_crc(uint32_t width, uint32_t height, int32_t dx, int32_t dy)
     return crc;
 }
 
+void remote_set_display_name(const char* name)
+{
+    int max_len=256;
+    if(strlen(name)<max_len)
+    {
+        max_len=strlen(name);
+    }
+    strncpy(RemoteHostVars.displayName, name, max_len);
+    RemoteHostVars.displayName[max_len]='\0';
+    EPHYR_DBG("DISPLAY name: %s",RemoteHostVars.displayName);
+}
+
 void *
 remote_screen_init(KdScreenInfo *screen,
                   int x, int y,
@@ -3097,10 +3195,10 @@ remote_screen_init(KdScreenInfo *screen,
     //but we need to reinstall it by the next screen init.
 
     EPHYR_DBG("REMOTE SCREEN INIT!!!!!!!!!!!!!!!!!!");
-    if(remoteVars.selstruct.callBackInstalled)
+    if(remoteVars.selstruct.threadStarted)
     {
-        EPHYR_DBG("SKIPPING CALLBACK INSTALL");
-        remoteVars.selstruct.callBackInstalled=FALSE;
+        EPHYR_DBG("SKIPPING Selection CALLBACK INSTALL");
+//         remoteVars.selstruct.callBackInstalled=FALSE;
     }
     else
     {
diff --git a/x2gokdriveremote.h b/x2gokdriveremote.h
index c574035..a06e692 100644
--- a/x2gokdriveremote.h
+++ b/x2gokdriveremote.h
@@ -259,40 +259,55 @@ struct sendqueue_element
     struct sendqueue_element* next;
 };
 
+//input selection
 typedef struct
 {
-    unsigned char* data;
-    uint32_t size;
-    int mimeData;
-    uint32_t position;
-    int target;
+    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;
+    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;
 
+
+//chunk of data with output selection
 typedef struct
 {
-    unsigned char* data;
-    uint32_t size;
-    int mimeData;
-    BOOL changed;
-
-}outputBuffer;
+    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;
 
 typedef struct
 {
-    BOOL readingIncremental;
-    uint32_t incrementalPosition;
-    Window clipWinId;
-    WindowPtr clipWinPtr;
-    BOOL callBackInstalled;
-    unsigned char selectionMode;
-//Output selection
-    outputBuffer clipboard;
-    outputBuffer selection;
-//Input selection
-    BOOL readingInputBuffer;
-    inputBuffer inBuffer;
-    inputBuffer inSelection;
-    inputBuffer inClipboard;
+    unsigned long selThreadId; //id of selection thread
+    enum ClipboardMode selectionMode; //CLIP_NONE, CLIP_CLIENT, CLIP_SERVER, CLIP_BOTH
+
+    //output selection members
+    uint32_t incrementalSize; //the total size of INCR selection we are currently reading
+    uint32_t incrementalSizeRead; //bytes already read
+    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
+    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
+
+    //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
+    pthread_mutex_t inMutex; //mutex for synchronization of incoming selection
 }SelectionStructure;
 
 
@@ -306,6 +321,7 @@ struct _remoteHostVars
     char stateFile[256];
     char acceptAddr[256];
     char cookie[33];
+    char displayName[256];
     int listenPort;
     int jpegQuality;
     uint32_t framenum;
@@ -374,7 +390,11 @@ struct _remoteHostVars
     SelectionStructure selstruct;
 } RemoteHostVars;
 
-int send_selection(int sel, char* data, uint32_t length, uint32_t mimeData);
+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);
+
+void readInputSelectionBuffer(char* buff);
+void readInputSelectionHeader(char* buff);
 
 #if XORG_VERSION_CURRENT < 11900000
 void pollEvents(void);
@@ -414,7 +434,7 @@ void clientReadNotify(int fd, int ready, void *data);
 void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t crc, uint32_t size);
 
 
-
+void clear_output_selection(void);
 
 void disconnect_client(void);
 
@@ -432,6 +452,7 @@ int remote_init(void);
 
 void remote_selection_init(void);
 
+void remote_set_display_name(const char* name);
 
 void *remote_screen_init(KdScreenInfo *screen,
                          int x, int y,
diff --git a/x2gokdriveselection.c b/x2gokdriveselection.c
index 39129c6..e174aff 100644
--- a/x2gokdriveselection.c
+++ b/x2gokdriveselection.c
@@ -25,659 +25,913 @@
  *
  */
 
+#include <xcb/xcb.h>
+#include <xcb/xfixes.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <png.h>
+
+
 #ifdef HAVE_CONFIG_H
 #include <dix-config.h>
 
 #if XORG_VERSION_CURRENT < 11999901
 #include <kdrive-config.h>
-#endif /* XORG_VERSION_CURRENT */
+#endif // XORG_VERSION_CURRENT
 
 #endif
-
 #include "x2gokdriveselection.h"
-
-#include "selection.h"
-#include <X11/Xatom.h>
-#include "propertyst.h"
-#include "xace.h"
-
 static struct _remoteHostVars *remoteVars = NULL;
 
-static Atom atomPrimary, atomClipboard, atomTargets, atomString, atomUTFString, atomTimestamp = {0};
-static Atom imageAtom = 0;
-static Atom atomJPEG, atomJPG = {0};
-
-static int (*proc_send_event_orig)(ClientPtr);
-static int (*proc_convert_selection_orig)(ClientPtr);
-static int (*proc_change_property_orig)(ClientPtr);
+static xcb_atom_t ATOM_CLIPBOARD;
 
-static int create_selection_window(void);
-
-int own_selection(int target)
+uint32_t max_chunk(void)
 {
-    Selection *pSel = NULL;
-    SelectionInfoRec info = {0};
-    Atom selection=atomPrimary;
-    int rc;
+    if(remoteVars->selstruct.clientSupportsExetndedSelection)
+        return 1024*100/4; //100KB
+    else
+        return 10*1024*1024/4; //10MB
+}
 
-    if(remoteVars->selstruct.selectionMode == CLIP_SERVER || remoteVars->selstruct.selectionMode == CLIP_NONE)
+int own_selection(enum SelectionType selection)
+{
+    xcb_atom_t sel=XCB_ATOM_PRIMARY;
+    if(remoteVars->selstruct.selectionMode == CLIP_NONE || remoteVars->selstruct.selectionMode == CLIP_SERVER)
     {
-        EPHYR_DBG("CLIENT selection disabled, not accepting selection event");
-        return Success;
+        EPHYR_DBG("Client selection is disabled");
+        return 0;
     }
-
-    if(target==CLIPBOARD)
-        selection=atomClipboard;
-
-
-    rc = create_selection_window();
-
-    if (rc != Success)
-        return rc;
-
-    rc = dixLookupSelection(&pSel, selection, serverClient, DixSetAttrAccess);
-    if (rc == Success)
+    if(selection!=PRIMARY)
     {
-        if (pSel->client && (pSel->client != serverClient))
-        {
-            xEvent event =
-            {
-                .u.selectionClear.time = currentTime.milliseconds,
-                .u.selectionClear.window = pSel->window,
-                .u.selectionClear.atom = pSel->selection
-            };
-            event.u.u.type = SelectionClear;
-            WriteEventsToClient(pSel->client, 1, &event);
-        }
+        sel=ATOM_CLIPBOARD;
     }
-    else if (rc == BadMatch)
-    {
-        pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
-        if (!pSel)
-            return BadAlloc;
+    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;
+    return 0;
+}
+void selection_init(struct _remoteHostVars *obj)
+{
+    remoteVars=obj;
+    return;
+}
 
-        pSel->selection = selection;
 
-        rc = XaceHookSelectionAccess(serverClient, &pSel,
-                                     DixCreateAccess | DixSetAttrAccess);
-        if (rc != Success)
-        {
-            free(pSel);
-            return rc;
-        }
+xcb_atom_t 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;
 
-        pSel->next = CurrentSelections;
-        CurrentSelections = pSel;
+    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);
     }
-    else
-        return rc;
-
-    pSel->lastTimeChanged = currentTime;
-    pSel->window = remoteVars->selstruct.clipWinId;
-    pSel->pWin = remoteVars->selstruct.clipWinPtr;
-    pSel->client = serverClient;
-
-    EPHYR_DBG("OWN selection: %s", NameForAtom(selection));
-
-    info.selection = pSel;
-    info.client = serverClient;
-    info.kind = SelectionSetOwner;
-    CallCallbacks(&SelectionCallback, &info);
-
-    return Success;
+    return a;
 }
 
-
-static int create_selection_window(void)
+char *atom_name(xcb_atom_t xatom)
 {
-    int result = -1;
-
-    if(remoteVars->selstruct.clipWinPtr)
-        return Success;
-
-
-    remoteVars->selstruct.clipWinId = FakeClientID(0);
-
-    remoteVars->selstruct.clipWinPtr = CreateWindow(remoteVars->selstruct.clipWinId, remoteVars->ephyrScreen->pScreen->root,
-                                         0, 0, 100, 100, 0, InputOnly,
-                                         0, NULL, 0, serverClient,
-                                         CopyFromParent, &result);
-    if (!remoteVars->selstruct.clipWinPtr)
+    //get name for atom, don't forget to free return value
+    char* name;
+    xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(remoteVars->selstruct.xcbConnection, xatom);
+    xcb_get_atom_name_reply_t *reply=xcb_get_atom_name_reply(remoteVars->selstruct.xcbConnection, cookie, NULL);
+
+    if(!reply)
+       return NULL;
+    if(!reply->name_len)
     {
-        EPHYR_DBG("Can't create selection window");
-        return result;
+        free(reply);
+        return NULL;
     }
-
-    if (!AddResource(remoteVars->selstruct.clipWinPtr->drawable.id, RT_WINDOW, remoteVars->selstruct.clipWinPtr))
-        return BadAlloc;
-
-    EPHYR_DBG("Selection window created %d, %p",remoteVars->selstruct.clipWinId, remoteVars->selstruct.clipWinPtr);
-    return Success;
+    name=malloc( xcb_get_atom_name_name_length(reply)+1);
+    strncpy(name, xcb_get_atom_name_name(reply),xcb_get_atom_name_name_length(reply));
+    name [xcb_get_atom_name_name_length(reply)]='\0';
+    free(reply);
+    return name;
 }
 
-
-static void request_selection(Atom selection, Atom rtype)
+static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, const char *name)
 {
-    xEvent ev = {{{0}}};
-    Selection *selPtr = NULL;
-    int rc = -1;
+    //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;
 
-    rc = create_selection_window();
-    if (rc != Success)
+    for (i = 0;i < size;i++)
     {
-        EPHYR_DBG("ERROR! Can't create selection Window");
-        return;
+        if (list[i]==a)
+            return a;
     }
-
-    /*EPHYR_DBG("Request: %s, %s",
-     *              NameForAtom(selection), NameForAtom(rtype));*/
-
-    rc = dixLookupSelection(&selPtr, selection, serverClient, DixGetAttrAccess);
-    if (rc != Success)
-        return;
-    ev.u.u.type = SelectionRequest;
-    ev.u.selectionRequest.owner = selPtr->window;
-    ev.u.selectionRequest.time = currentTime.milliseconds;
-    ev.u.selectionRequest.requestor = remoteVars->selstruct.clipWinId;
-    ev.u.selectionRequest.selection = selection;
-    ev.u.selectionRequest.target = rtype;
-    ev.u.selectionRequest.property = rtype;
-    WriteEventsToClient(selPtr->client, 1, &ev);
+    return 0;
 }
 
-
-
-static void selection_callback(CallbackListPtr *callbacks,
-                               void * data, void * args)
+static int is_string_atom( xcb_atom_t at)
 {
-    SelectionInfoRec *info = (SelectionInfoRec *) args;
-
-    if (info->kind != SelectionSetOwner)
-        return;
-    if (info->client == serverClient)
-        return;
-
-    /*
-     *    EPHYR_DBG("Selection owner changed: %s",
-     *              NameForAtom(info->selection->selection));*/
-
-    if ((info->selection->selection != atomPrimary) &&
-        (info->selection->selection != atomClipboard))
-        return;
-
-    if(remoteVars->selstruct.selectionMode == CLIP_BOTH || remoteVars->selstruct.selectionMode == CLIP_SERVER)
-       request_selection(info->selection->selection, atomTargets);
+    //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;
+    return 0;
 }
 
-static Atom find_atom_by_name(const char* name, const Atom list[], size_t size)
+static int is_image_atom( xcb_atom_t at)
 {
-    for (int i = 0;i < size;i++)
-    {
-        if(!strcmp(name, NameForAtom (list[i])))
-        {
-            EPHYR_DBG("Found IMAGE ATOM %s:%d", NameForAtom (list[i]), list[i]);
-            return list [i];
-        }
-    }
+    //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"))
+        return 1;
     return 0;
 }
 
-static BOOL find_image_atom(const Atom list[], size_t size)
+static xcb_atom_t best_atom_from_target(xcb_atom_t* list, size_t size)
 {
-    Atom at = {0};
-    at=find_atom_by_name("image/jpg",list,size);
-    if(at)
+    //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")))
+    {
+//         EPHYR_DBG("selecting mime type UTF8_STRING");
+        return a;
+    }
+    if((a=target_has_atom(list, size, "text/plain;charset=utf-8")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG("selecting mime type text/plain;charset=utf-8");
+        return a;
     }
-    at=find_atom_by_name("image/jpeg",list,size);
-    if(at)
+    if((a=target_has_atom(list, size, "STRING")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG( "selecting mime type STRING");
+        return a;
     }
-    at=find_atom_by_name("image/png",list,size);
-    if(at)
+    if((a=target_has_atom(list, size, "TEXT")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG( "selecting mime type TEXT");
+        return a;
     }
-    at=find_atom_by_name("image/bmp",list,size);
-    if(at)
+    if((a=target_has_atom(list, size, "text/plain")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG( "selecting mime type text/plain");
+        return a;
     }
-    at=find_atom_by_name("image/xpm",list,size);
-    if(at)
+
+    //selecting loseless formats first
+    if((a=target_has_atom(list, size, "image/png")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG( "selecting mime type image/png");
+        return a;
     }
-    at=find_atom_by_name("PIXMAP",list,size);
-    if(at)
+    if((a=target_has_atom(list, size, "image/xpm")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG( "selecting mime type image/xpm");
+        return a;
     }
-    at=find_atom_by_name("image/ico",list,size);
-    if(at)
+    if((a=target_has_atom(list, size, "PIXMAP")))
     {
-        imageAtom=at;
-        return TRUE;
+//         EPHYR_DBG( "selecting mime type PIXMAP");
+        return a;
     }
-
-    imageAtom=0;
-    return FALSE;
-
+    if((a=target_has_atom(list, size, "image/bmp")))
+    {
+//         EPHYR_DBG( "selecting mime type image/bmp");
+        return a;
+    }
+    if((a=target_has_atom(list, size, "image/jpg")))
+    {
+//         EPHYR_DBG( "selecting mime type image/jpg");
+        return a;
+    }
+    if((a=target_has_atom(list, size, "image/jpeg")))
+    {
+//         EPHYR_DBG( "selecting mime type image/jpeg");
+        return a;
+    }
+    return 0;
 }
 
-//static void listAtoms(const Atom list[], size_t size)
-//{
-//    size_t i;
-//
-//    for (i = 0;i < size;i++)
-//    {
-//        EPHYR_DBG("%d:%s", list[i], NameForAtom( list[i]));
-//    }
-//}
-
-static Bool prop_has_atom(Atom atom, const Atom list[], size_t size)
+void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t)
 {
-    size_t i = 0;
-
-    for (i = 0;i < size;i++) {
-        if (list[i] == atom)
-            return TRUE;
+    //execute convert selection for primary or clipboard to get mimetypes or data (depends on target atom)
+    if(!t)
+        t=XCB_CURRENT_TIME;
+    if(property)
+    {
+        xcb_delete_property(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId,property);
     }
-
-    return FALSE;
+    xcb_convert_selection(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId,selection, target, property, t);
+    xcb_flush(remoteVars->selstruct.xcbConnection);
 }
 
-static void process_selection(Atom selection, Atom target,
-                              Atom property, Atom requestor,
-                              TimeStamp time)
+void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
 {
-    PropertyPtr prop;
-    int rc = -1;
-
-    rc = dixLookupProperty(&prop, remoteVars->selstruct.clipWinPtr, property,
-                           serverClient, DixReadAccess);
-
-    if(rc== BadMatch)
+    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;
+
+
+    //request property which represents value of selection (data or mime types)
+    //get max 100K of data, we don't need to send more than that over network for perfomance reasons
+    cookie= xcb_get_property(remoteVars->selstruct.xcbConnection,FALSE, remoteVars->selstruct.clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, 0, max_chunk());
+    reply=xcb_get_property_reply(remoteVars->selstruct.xcbConnection, cookie, NULL);
+    if(!reply)
     {
-        EPHYR_DBG("BAD MATCH!!!");
+//         EPHYR_DBG( "NULL reply");
     }
-
-    if (rc != Success)
-        return;
-
-    EPHYR_DBG("Selection notification for %s (target %s, property %s, type %s)",
-              NameForAtom(selection), NameForAtom(target),
-              NameForAtom(property), NameForAtom(prop->type));
-
-    if (target != property)
-        return;
-
-    if (target == atomTargets)
+    else
     {
-        if (prop->format != 32)
-            return;
-        if (prop->type != XA_ATOM)
-            return;
-
-//        listAtoms((const Atom*)prop->data, prop->size);
-
-
-        if(prop_has_atom(atomUTFString, (const Atom*)prop->data, prop->size))
-        {
-            request_selection(selection, atomUTFString);
-        }
-        else if(prop_has_atom(atomString, (const Atom*)prop->data, prop->size))
+        if(reply->type==XCB_NONE)
         {
-            request_selection(selection, atomString);
+//             EPHYR_DBG( "NONE reply");
         }
         else
         {
-            /* requesting pixmap only for clipboard */
-            if((selection == atomClipboard) && find_image_atom((const Atom*)prop->data, prop->size) && imageAtom)
+            //here we have type of data
+
+/*
+            stype=atom_name(reply->type);
+            sprop=atom_name(property);
+            EPHYR_DBG( "Property %s type %s, format %d, length %d", sprop, stype, reply->format, reply->length);
+            if(stype)
+                free(stype);
+            if(sprop)
+                free(sprop);
+*/
+            //need to read property incrementally
+            if(reply->type == atom("INCR"))
+            {
+                unsigned int sz=*((unsigned int*) xcb_get_property_value(reply));
+//                 EPHYR_DBG( "have incr property size: %d", sz);
+                remoteVars->selstruct.incrAtom=property;
+                remoteVars->selstruct.incrementalSize=sz;
+                remoteVars->selstruct.incrementalSizeRead=0;
+
+                //deleteing property should tell the selection owner that we are ready for incremental reading of data
+                xcb_delete_property(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId, property);
+                xcb_flush(remoteVars->selstruct.xcbConnection);
+                free(reply);
+                return;
+            }
+            //we have supported mime types in reply
+            if(reply->type == atom( "ATOM"))
             {
-                request_selection(selection, imageAtom);
+                if(reply->format!=32)
+                {
+                    EPHYR_DBG( "wrong format for TARGETS");
+                }
+                else
+                {
+//                     EPHYR_DBG( "target supports %lu mime types",xcb_get_property_value_length(reply)/sizeof(xcb_atom_t));
+
+                    /*
+                    #warning debug
+                    tg=(xcb_atom_t*) xcb_get_property_value(reply);
+                    for(int i=0;i<xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);++i)
+                    {
+                        stype=atom_name(tg[i]);
+                        EPHYR_DBG("selection  has target: %s, %d",stype, tg[i]);
+                        if(stype)
+                            free(stype);
+                    }
+                    //#debug
+                    */
+
+                    data_atom=0;
+                    //get the best of supported mime types and request the selection in this format
+                    data_atom=best_atom_from_target(xcb_get_property_value(reply), xcb_get_property_value_length(reply)/sizeof(xcb_atom_t));
+
+                    xcb_delete_property( remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, property);
+                    xcb_flush(remoteVars->selstruct.xcbConnection);
+
+                    if(data_atom)
+                        request_selection_data( selection, data_atom, data_atom, 0);
+                    else
+                    {
+                        EPHYR_DBG( "there are no supported mime types in the target");
+                    }
+                }
             }
+            else
+            {
+                //here we have selection as string or image
+                if(is_image_atom( reply->type) || is_string_atom( reply->type))
+                {
+                    //read property data in loop in the chunks with size (100KB)
+                    do
+                    {
+                        bytes_left=reply->bytes_after;
+                        //now we can access property data
+
+                        /*FILE* cp=fopen("/tmp/clip", "a");
+                        fwrite(xcb_get_property_value(reply),1, xcb_get_property_value_length(reply),cp);
+                        fclose(cp);*/
+
+
+                        chunk=(outputChunk*) malloc(sizeof(outputChunk));
+
+                        memset((void*)chunk,0,sizeof(outputChunk));
+
+                        if(xcb_get_property_value_length(reply))
+                        {
+                            chunk->size=xcb_get_property_value_length(reply);
+                            chunk->data=(unsigned char*)malloc(chunk->size);
+                            memcpy(chunk->data, xcb_get_property_value(reply),chunk->size);
+                        }
+
+                        chunk->compressed=FALSE;
+                        if(is_string_atom(property))
+                            chunk->mimeData=UTF_STRING;
+                        else
+                            chunk->mimeData=PIXMAP;
+
+                        chunk->selection=selection_from_atom(selection);
+
+
+                        if(remoteVars->selstruct.incrementalSize && (remoteVars->selstruct.incrAtom==property))
+                        {
+                            //this is the total size of our selection
+                            chunk->totalSize=remoteVars->selstruct.incrementalSize;
+                            //we are doing incremental reading
+                            if(remoteVars->selstruct.incrementalSizeRead == 0)
+                            {
+                                //it's the first chunk
+                                chunk->firstChunk=TRUE;
+                            }
+                            remoteVars->selstruct.incrementalSizeRead+=xcb_get_property_value_length(reply);
+                            if(!bytes_left && ! bytes_read && !xcb_get_property_value_length(reply))
+                            {
+                                //we got the property with 0 size it means that we recieved all data of incr property
+//                                 EPHYR_DBG("INCR Property done, read %d", remoteVars->selstruct.incrementalSizeRead);
+                                remoteVars->selstruct.incrAtom=0;
+                                remoteVars->selstruct.incrementalSize=0;
+                                //it's the last chunk
+                                chunk->lastChunk=TRUE;
+                            }
+                        }
+                        else
+                        {
+                            //we are doing simple read
+                            if(bytes_read==0)
+                            {
+                                //it's the first chunk
+                                chunk->firstChunk=TRUE;
+                            }
+                            if(bytes_left==0)
+                            {
+                                //the last chunk
+                                chunk->lastChunk=TRUE;
+                            }
+                            //total size of the selection
+                            chunk->totalSize=xcb_get_property_value_length(reply)+bytes_left;
+                        }
+
+                        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);
+
+                        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=(struct outputChunk*)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)
+                        {
+                            free(reply);
+                            cookie= xcb_get_property(remoteVars->selstruct.xcbConnection, 0, remoteVars->selstruct.clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, bytes_read/4, max_chunk());
+                            reply=xcb_get_property_reply(remoteVars->selstruct.xcbConnection, cookie, NULL);
+                            if(!reply)
+                            {
+                                //something is wrong
+                                EPHYR_DBG("NULL reply");
+                                break;
+                            }
+                        }
+                        //read in loop till no data left
+                    }while(bytes_left);
+                }
+                else
+                {
+                    stype=atom_name(reply->type);
+                    EPHYR_DBG("Not supported mime type: %s, %d",stype, reply->type);
+                    if(stype)
+                        free(stype);
+                }
+            }
+            if(reply)
+                free(reply);
+            //if reading incr property this will say sel owner that we are ready for the next chunk of data
+            xcb_delete_property(remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, property);
+            xcb_flush(remoteVars->selstruct.xcbConnection);
         }
     }
-    else if (target == atomString || target == atomUTFString  || (target==imageAtom && imageAtom) )
+}
+
+void process_selection_notify(xcb_generic_event_t *e)
+{
+    xcb_selection_notify_event_t *sel_event;
+
+//     EPHYR_DBG("selection notify");
+    sel_event=(xcb_selection_notify_event_t *)e;
+
+    //processing the event which is reply for convert selection call
+
+    remoteVars->selstruct.incrAtom=0;
+    remoteVars->selstruct.incrementalSize=0;
+
+    if (sel_event->requestor != remoteVars->selstruct.clipWinId)
     {
-        int format=STRING;
-        if(target==atomUTFString)
-        {
-            format=UTF_STRING;
-        }else if(target == imageAtom)
+//         EPHYR_DBG("not our window");
+        return;
+    }
+    else
+    {
+//         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)
         {
-            format=PIXMAP;
+            EPHYR_DBG( "NO SELECTION");
         }
-        /* read incrementinal data only for clipboard */
-        if(!strcmp( NameForAtom(prop->type), "INCR") && selection==atomClipboard)
+        else
         {
-            EPHYR_DBG("GOT INCR PROPERTY: %d",*((int*)prop->data));
-            remoteVars->selstruct.readingIncremental=TRUE;
-            pthread_mutex_lock(&remoteVars->sendqueue_mutex);
-
-            remoteVars->selstruct.clipboard.size=*((int*)prop->data);
-            remoteVars->selstruct.clipboard.data=malloc(remoteVars->selstruct.clipboard.size);
-            remoteVars->selstruct.clipboard.mimeData=format;
-            remoteVars->selstruct.incrementalPosition=0;
-            pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
-
-            DeleteProperty(serverClient,remoteVars->selstruct.clipWinPtr, property);
-            return;
+            remoteVars->selstruct.currentSelection=sel_event->selection;
+            //read property
+            read_selection_property(remoteVars->selstruct.currentSelection, sel_event->property);
         }
+    }
+}
 
-        if (prop->format != 8)
-            return;
-        if (prop->type != atomString && prop->type != atomUTFString && prop->type!=imageAtom)
-            return;
+void process_property_notify(xcb_generic_event_t *e)
+{
+    xcb_property_notify_event_t *pn;
 
-        pthread_mutex_lock(&remoteVars->sendqueue_mutex);
+//     EPHYR_DBG("property notify");
 
-        remoteVars->selstruct.readingIncremental=FALSE;
-        if(selection==atomClipboard || selection ==atomPrimary)
+    pn = (xcb_property_notify_event_t *)e;
+    if (pn->window != remoteVars->selstruct.clipWinId)
+    {
+//         EPHYR_DBG("not our window");
+        return;
+    }
+//     EPHYR_DBG("property %d, state %d ", pn->atom, pn->state);
+    if(pn->state==XCB_PROPERTY_NEW_VALUE)
+    {
+//         EPHYR_DBG( "NEW VALUE");
+        if(remoteVars->selstruct.incrAtom==pn->atom && remoteVars->selstruct.incrementalSize)
         {
-            outputBuffer* buff;
-            if(selection==atomClipboard)
-                buff=&remoteVars->selstruct.clipboard;
-            else
-                buff=&remoteVars->selstruct.selection;
-            if(buff->data)
-            {
-                free(buff->data);
-            }
-            buff->size=prop->size;
-            buff->data=malloc(buff->size);
-            memcpy(buff->data, prop->data, prop->size);
-            buff->changed=TRUE;
-            buff->mimeData=format;
-//            EPHYR_DBG("Have new Clipboard %s %d",remoteVars->selstruct.clipboard, remoteVars->selstruct.clipboardSize);
+            //we recieveing the selection data incrementally, let's read a next chunk
+//             EPHYR_DBG("reading incr property %d", pn->atom);
+            read_selection_property(remoteVars->selstruct.currentSelection, pn->atom);
         }
-        pthread_cond_signal(&remoteVars->have_sendqueue_cond);
-        pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
     }
-    DeleteProperty(serverClient,remoteVars->selstruct.clipWinPtr, property);
+    if(pn->state==XCB_PROPERTY_DELETE)
+    {
+//         EPHYR_DBG( "DELETE");
+    }
+    if(pn->state==XCB_PROPERTY_NOTIFY)
+    {
+//         EPHYR_DBG( "NOTIFY");
+    }
 }
 
 
-
-
-#define SEND_EVENT_BIT 0x80
-
-static int send_event(ClientPtr client)
+void process_selection_owner_notify(xcb_generic_event_t *e)
 {
-    REQUEST(xSendEventReq);
-    REQUEST_SIZE_MATCH(xSendEventReq);
-
-    stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
+    enum SelectionType selection;
+    xcb_xfixes_selection_notify_event_t *notify_event=(xcb_xfixes_selection_notify_event_t *)e;
 
-
-    if (stuff->event.u.u.type == SelectionNotify &&
-        stuff->event.u.selectionNotify.requestor == remoteVars->selstruct.clipWinId)
+//     EPHYR_DBG("SEL OWNER notify, selection: %d, window: %d, owner: %d",notify_event->selection, notify_event->window, notify_event->owner);
+    if(notify_event->owner == remoteVars->selstruct.clipWinId)
     {
-        TimeStamp time;
-        time = ClientTimeToServerTime(stuff->event.u.selectionNotify.time);
-
-        process_selection(stuff->event.u.selectionNotify.selection,
-                          stuff->event.u.selectionNotify.target,
-                          stuff->event.u.selectionNotify.property,
-                          stuff->event.u.selectionNotify.requestor,
-                          time);
+//         EPHYR_DBG("It's our own selection, ignoring");
+        return;
     }
 
-    return proc_send_event_orig(client);
-}
-
-static int convert_selection(ClientPtr client, Atom selection,
-                               Atom target, Atom property,
-                               Window requestor, CARD32 time)
-{
-    Selection *pSel = NULL;
-    WindowPtr pWin = {0};
-    int rc = -1;
-
-    Atom realProperty = {0};
-    xEvent event = {{{0}}};
+    if(remoteVars->selstruct.selectionMode == CLIP_NONE || remoteVars->selstruct.selectionMode == CLIP_CLIENT)
+    {
+        EPHYR_DBG("Server selection is disabled");
+        return;
+    }
 
-    inputBuffer* buff=&remoteVars->selstruct.inSelection;
-    if(selection==atomClipboard)
-        buff=&remoteVars->selstruct.inClipboard;
+    //cancel all previous incr reading
+    remoteVars->selstruct.incrementalSize=remoteVars->selstruct.incrementalSizeRead=0;
+    remoteVars->selstruct.incrAtom=0;
 
-//    EPHYR_DBG("Selection request for %s (type %s)",  NameForAtom(selection), NameForAtom(target));
 
-    rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
-    if (rc != Success)
-        return rc;
+    selection=selection_from_atom(notify_event->selection);
 
-    rc = dixLookupWindow(&pWin, requestor, client, DixSetAttrAccess);
-    if (rc != Success)
-        return rc;
+    //we are not owners of this selction anymore
+    remoteVars->selstruct.inSelection[selection].owner=FALSE;
 
-    if (property != None)
-        realProperty = property;
-    else
-        realProperty = target;
+    //get supported mime types
+    request_selection_data( notify_event->selection, atom( "TARGETS"), atom( "TARGETS"), 0);
 
+}
 
-    if (target == atomTargets)
+static
+void *selection_thread (void* id)
+{
+    xcb_screen_t        *screen;
+    uint response_type;
+    xcb_generic_event_t *e=NULL;
+    uint32_t             mask = 0;
+    uint32_t             values[2];
+    xcb_generic_error_t *error = 0;
+    const xcb_query_extension_reply_t *reply;
+    xcb_xfixes_query_version_cookie_t xfixes_query_cookie;
+    xcb_xfixes_query_version_reply_t *xfixes_query;
+
+
+    /* Create the window */
+    remoteVars->selstruct.xcbConnection = xcb_connect (RemoteHostVars.displayName, NULL);
+    if(xcb_connection_has_error(remoteVars->selstruct.xcbConnection))
+    {
+        EPHYR_DBG("Warning! can't create XCB connection to display %s, selections exchange between client and server will be disabled", RemoteHostVars.displayName);
+        remoteVars->selstruct.xcbConnection=0;
+        pthread_exit(0);
+        return NULL;
+    }
+    screen = xcb_setup_roots_iterator (xcb_get_setup (remoteVars->selstruct.xcbConnection)).data;
+    remoteVars->selstruct.clipWinId = xcb_generate_id (remoteVars->selstruct.xcbConnection);
+    mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+    values[0] = screen->white_pixel;
+    values[1] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+
+    ATOM_CLIPBOARD=atom("CLIPBOARD");
+
+    //create window which will recieve selection events and provide remote selection to X-clients
+    xcb_create_window (remoteVars->selstruct.xcbConnection,
+                       XCB_COPY_FROM_PARENT,
+                       remoteVars->selstruct.clipWinId,
+                       screen->root,
+                       0, 0,
+                       1, 1,
+                       0,
+                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                       screen->root_visual,
+                       mask, values);
+
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+    //check if we have xfixes, we need it to recieve selection owner events
+    reply = xcb_get_extension_data(remoteVars->selstruct.xcbConnection, &xcb_xfixes_id);
+    if (reply && reply->present)
     {
-        Atom string_targets[] = { atomTargets, atomTimestamp, atomUTFString };
-        Atom pixmap_targets[] = { atomTargets, atomTimestamp, atomJPEG, atomJPG };
-        if(buff->mimeData == PIXMAP)
+        xfixes_query_cookie = xcb_xfixes_query_version(remoteVars->selstruct.xcbConnection, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
+        xfixes_query = xcb_xfixes_query_version_reply (remoteVars->selstruct.xcbConnection, xfixes_query_cookie, &error);
+        if (!xfixes_query || error || xfixes_query->major_version < 2)
         {
-            rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
-                                         XA_ATOM, 32, PropModeReplace,
-                                         sizeof(pixmap_targets)/sizeof(pixmap_targets[0]),
-                                         pixmap_targets, TRUE);
+            free(error);
         }
         else
         {
-            rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
-                                         XA_ATOM, 32, PropModeReplace,
-                                         sizeof(string_targets)/sizeof(string_targets[0]),
-                                         string_targets, TRUE);
+            //we'll recieve sel owner events for primary amd clipboard
+            mask =  XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER;
+            xcb_xfixes_select_selection_input_checked(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId, XCB_ATOM_PRIMARY, mask);
+            xcb_xfixes_select_selection_input_checked(remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, ATOM_CLIPBOARD, mask);
         }
-        if (rc != Success)
-            return rc;
+        free(xfixes_query);
     }
-    else if (target == atomTimestamp)
-    {
-        rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
-                                     XA_INTEGER, 32, PropModeReplace, 1,
-                                     &pSel->lastTimeChanged.milliseconds,
-                                     TRUE);
-        if (rc != Success)
-            return rc;
-    }
-    else if (target == atomUTFString || target == atomJPEG || target == atomJPG )
-    {
-        rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
-                                     target, 8, PropModeReplace,
-                                     buff->size, buff->data, TRUE);
-        if (rc != Success)
-            return rc;
-    }
-    else
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+
+    // event loop
+    while ((e = xcb_wait_for_event(remoteVars->selstruct.xcbConnection)))
     {
-        return BadMatch;
+        response_type = e->response_type & ~0x80;
+
+        //we notified that selection is changed in primary or clipboard
+        if (response_type == reply->first_event + XCB_XFIXES_SELECTION_NOTIFY)
+        {
+            process_selection_owner_notify(e);
+        }
+        else
+        {
+            //we notified that property is changed
+            if (response_type == XCB_PROPERTY_NOTIFY)
+            {
+                process_property_notify(e);
+            }
+            //we got reply to our selection request (mime types or data)
+            else if (response_type == XCB_SELECTION_NOTIFY)
+            {
+                process_selection_notify(e);
+            }
+            else if (response_type == XCB_SELECTION_REQUEST)
+            {
+                process_selection_request(e);
+            }
+            else
+            {
+//                 EPHYR_DBG("not processing this event %d ", response_type);
+            }
+        }
+        free(e);
+        xcb_flush(remoteVars->selstruct.xcbConnection);
     }
 
-    event.u.u.type = SelectionNotify;
-    event.u.selectionNotify.time = time;
-    event.u.selectionNotify.requestor = requestor;
-    event.u.selectionNotify.selection = selection;
-    event.u.selectionNotify.target = target;
-    event.u.selectionNotify.property = property;
-    WriteEventsToClient(client, 1, &event);
-    return Success;
+    pthread_exit(0);
+    return NULL;
 }
 
 
-static int proc_convert_selection(ClientPtr client)
+void install_selection_callbacks(void)
 {
-    Bool paramsOkay;
-    WindowPtr pWin = {0};
-    Selection *pSel = NULL;
-    int rc = -1;
-
-    REQUEST(xConvertSelectionReq);
-    REQUEST_SIZE_MATCH(xConvertSelectionReq);
-    rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess);
-    if (rc != Success)
-       return rc;
-    paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
-    paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
-    if (!paramsOkay)
-    {
-        client->errorValue = stuff->property;
-        return BadAtom;
-    }
-
-    rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
-    if (rc == Success && pSel->client == serverClient &&  pSel->window == remoteVars->selstruct.clipWinId)
-    {
-        rc = convert_selection(client, stuff->selection,
-                                 stuff->target, stuff->property,
-                                 stuff->requestor, stuff->time);
-        if (rc != Success)
-        {
-            xEvent event;
-            memset(&event, 0, sizeof(xEvent));
-            event.u.u.type = SelectionNotify;
-            event.u.selectionNotify.time = stuff->time;
-            event.u.selectionNotify.requestor = stuff->requestor;
-            event.u.selectionNotify.selection = stuff->selection;
-            event.u.selectionNotify.target = stuff->target;
-            event.u.selectionNotify.property = None;
-            WriteEventsToClient(client, 1, &event);
-        }
+    int ret;
+    if(remoteVars->selstruct.threadStarted)
+        return;
+    remoteVars->selstruct.threadStarted=TRUE;
 
-        return Success;
+    ret = pthread_create(&(remoteVars->selstruct.selThreadId), NULL, selection_thread, (void *)remoteVars->selstruct.selThreadId);
+    if (ret)
+    {
+        EPHYR_DBG("ERROR; return code from pthread_create() is %d", ret);
+        remoteVars->selstruct.selThreadId=0;
     }
-
-    return proc_convert_selection_orig(client);
+    else
+        pthread_mutex_init(&remoteVars->selstruct.inMutex, NULL);
+    return;
 }
 
-
-static int proc_change_property(ClientPtr client)
+void process_selection_request(xcb_generic_event_t *e)
 {
-    int rc = -1;
-    BOOL incRead;
-    PropertyPtr prop = {0};
+    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;
 
-    REQUEST(xChangePropertyReq);
+    xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
+    event->response_type = XCB_SELECTION_NOTIFY;
+    event->requestor = req->requestor;
+    event->selection = req->selection;
+    event->target    = req->target;
+    event->property  = XCB_NONE;
+    event->time      = req->time;
 
-    REQUEST_AT_LEAST_SIZE(xChangePropertyReq);
 
-    rc=proc_change_property_orig(client);
+    if(property == XCB_NONE)
+        property=target;
 
-    if(rc!=Success)
-        return rc;
 
+/*
+    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");
+        xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+        xcb_flush(remoteVars->selstruct.xcbConnection);
+        free(event);
+        return;
+    }
 
-    pthread_mutex_lock(&remoteVars->sendqueue_mutex);
-    incRead=remoteVars->selstruct.readingIncremental;
-    pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
-
+    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");
+        xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+        xcb_flush(remoteVars->selstruct.xcbConnection);
+        free(event);
+        return;
+    }
+    if(req->target==atom("TIMESTAMP"))
+    {
+        event->property=property;
+//         EPHYR_DBG("requested TIMESTAMP");
+        xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor,
+                            property, XCB_ATOM_INTEGER, 32, 1, &remoteVars->selstruct.inSelection[sel].timestamp);
 
-    if(stuff->window == remoteVars->selstruct.clipWinId && incRead &&
-        ((imageAtom && imageAtom==stuff->type)||(stuff->type == atomUTFString) || (stuff->type == atomString)) )
-        EPHYR_DBG("HAVE NEW DATA for %d: %s %s", stuff->window, NameForAtom(stuff->property), NameForAtom(stuff->type));
+    }
+    else if(req->target==atom("TARGETS"))
+    {
+        event->property=property;
+//         EPHYR_DBG("requested TARGETS");
+        send_mime_types(req);
+    }
     else
-        return rc;
-
+    {
+        event->property=send_data(req);
+    }
+    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);
+}
 
+enum SelectionType selection_from_atom(xcb_atom_t selection)
+{
+    if(selection == XCB_ATOM_PRIMARY)
+        return PRIMARY;
+    return CLIPBOARD;
 
-    rc = dixLookupProperty(&prop, remoteVars->selstruct.clipWinPtr, stuff->property,
-                           serverClient, DixReadAccess);
+}
 
-    if(rc== BadMatch)
-    {
-        EPHYR_DBG("BAD MATCH!!!");
-        return Success;
-    }
+void send_mime_types(xcb_selection_request_event_t* req)
+{
+    //inmutex is locked in the caller function
+    //send supported targets
+    enum SelectionType sel=selection_from_atom(req->selection);
 
-    if (rc != Success)
-        return rc;
+    //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;
 
-    EPHYR_DBG("Have %d bytes for %s ",
-              prop->size, NameForAtom(stuff->property));
+    if((a=atom("TARGETS")))
+        targets[mcount++]=a;
+    if((a=atom("TIMESTAMP")))
+        targets[mcount++]=a;
 
-    pthread_mutex_lock(&remoteVars->sendqueue_mutex);
-    if(prop->size==0)
+    if(remoteVars->selstruct.inSelection[sel].mimeData==PIXMAP)
     {
-        EPHYR_DBG("READ %d FROM %d", remoteVars->selstruct.incrementalPosition, remoteVars->selstruct.clipboard.size);
-        remoteVars->selstruct.readingIncremental=FALSE;
-        if(remoteVars->selstruct.incrementalPosition == remoteVars->selstruct.clipboard.size)
+        //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;
+        }
+        else
         {
-            remoteVars->selstruct.clipboard.changed=TRUE;
-            pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+            if((a=atom("image/jpg")))
+                targets[mcount++]=a;
+            if((a=atom("image/jpeg")))
+                targets[mcount++]=a;
         }
     }
     else
     {
-        memcpy(remoteVars->selstruct.clipboard.data+remoteVars->selstruct.incrementalPosition, prop->data, prop->size);
-        remoteVars->selstruct.incrementalPosition+=prop->size;
+        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;
     }
 
-    pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
-
+    xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, XCB_ATOM_ATOM,
+                        32, mcount, (const void *)targets);
+    xcb_flush(remoteVars->selstruct.xcbConnection);
+}
 
-    DeleteProperty(serverClient,remoteVars->selstruct.clipWinPtr, stuff->property);
+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");
 
-    return rc;
+    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))
+        {
+//             EPHYR_DBG("sending UTF text");
+            return set_data_property(req, remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size);
+        }
+        else
+        {
+            starget=atom_name(req->target);
+            EPHYR_DBG("unsupported property requested: %s",starget);
+            if(starget)
+                free(starget);
+            return XCB_NONE;
+        }
+    }
+    else
+    {
+        if(!is_image_atom(req->target))
+        {
+            starget=atom_name(req->target);
+            EPHYR_DBG("unsupported property requested: %s",starget);
+            if(starget)
+                free(starget);
+            return XCB_NONE;
+        }
+/*
+        starget=atom_name(req->target);
+        EPHYR_DBG("requested %s",starget);
+        if(starget)
+            free(starget);
+*/
+
+        //TODO: implement convertion between different image formats
+        if(is_png(remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size))
+        {
+            if(req->target!=png)
+            {
+                starget=atom_name(req->target);
+                EPHYR_DBG("unsupported property requested: %s",starget);
+                if(starget)
+                    free(starget);
+                return XCB_NONE;
+            }
+        }
+        else
+        {
+            if((req->target!=jpg)&&(req->target!=jpeg))
+            {
+                starget=atom_name(req->target);
+                EPHYR_DBG("unsupported property requested: %s",starget);
+                if(starget)
+                    free(starget);
+                return XCB_NONE;
+            }
+        }
+        return set_data_property(req, remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size);
+    }
+    return XCB_NONE;
 }
 
-void selection_init(struct _remoteHostVars *obj)
+xcb_atom_t set_data_property(xcb_selection_request_event_t* req, unsigned char* data, uint32_t size)
 {
-    EPHYR_DBG("INITIALIZING selections");
-    remoteVars=obj;
-    proc_convert_selection_orig = ProcVector[X_ConvertSelection];
-    proc_send_event_orig = ProcVector[X_SendEvent];
-    proc_change_property_orig = ProcVector[X_ChangeProperty];
-    ProcVector[X_ConvertSelection] = proc_convert_selection;
-    ProcVector[X_SendEvent] = send_event;
-    ProcVector[X_ChangeProperty] = proc_change_property;
-    remoteVars->selstruct.callBackInstalled=TRUE;
-    install_selection_callbacks();
-}
+    //inmutex locked in parent thread
+    //set data to window property
 
+    //change when implemented
+    BOOL support_incr=FALSE;
 
-void install_selection_callbacks(void)
-{
-    if(remoteVars->selstruct.selectionMode == CLIP_CLIENT || remoteVars->selstruct.selectionMode == CLIP_NONE)
+    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 )
     {
-        EPHYR_DBG("SERVER CLIPBOARD disabled, not installing callbacks");
-        return;
+        EPHYR_DBG("property %d doesn't support INCR",req->property);
+        support_incr=FALSE;
     }
 
-    atomPrimary = MakeAtom("PRIMARY", 7, TRUE);
-    atomClipboard = MakeAtom("CLIPBOARD", 9, TRUE);
-
-    atomTargets = MakeAtom("TARGETS", 7, TRUE);
-    atomTimestamp = MakeAtom("TIMESTAMP", 9, TRUE);
-    atomString = MakeAtom("STRING", 6, TRUE);
-    atomUTFString = MakeAtom("UTF8_STRING", 11, TRUE);
-
-    atomJPEG = MakeAtom("image/jpeg",487,TRUE);
-    atomJPG = MakeAtom("image/jpg",488,TRUE);
+    //check if we are sending incr
+    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);
 
-    remoteVars->selstruct.clipWinPtr=0;
+        xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
+                            8, size, (const void *)data);
 
-    /* try to dele callback to avaid double call */
-    DeleteCallback(&SelectionCallback, selection_callback, 0);
+        xcb_flush(remoteVars->selstruct.xcbConnection);
+        return req->property;
+    }
 
-    if (!AddCallback(&SelectionCallback, selection_callback, 0))
-        FatalError("Failed to install callback\n");
-    else
-        EPHYR_DBG("Selection callback installed");
+    EPHYR_DBG("data is too big");
+    return XCB_NONE;
+}
 
+BOOL is_png(unsigned char* data, uint32_t size)
+{
+    if( size<8)
+        return FALSE;
+    return !png_sig_cmp(data, 0, 8);
 }
diff --git a/x2gokdriveselection.h b/x2gokdriveselection.h
index 3827ffa..7ec8d8a 100644
--- a/x2gokdriveselection.h
+++ b/x2gokdriveselection.h
@@ -30,8 +30,24 @@
 
 #include "x2gokdriveremote.h"
 
+uint32_t max_chunk(void);
+
 void selection_init(struct _remoteHostVars *obj);
 void install_selection_callbacks(void);
-int own_selection(int target);
+int own_selection(enum SelectionType selection);
+
+xcb_atom_t atom(const char* name);
+char *atom_name(xcb_atom_t xatom);
+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);
+void send_mime_types(xcb_selection_request_event_t* req);
+enum SelectionType selection_from_atom(xcb_atom_t selection);
+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);
+BOOL is_png(unsigned char* data, uint32_t 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