[X2Go-Commits] [x2gokdriveclient] 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:44 CEST 2020


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

x2go pushed a commit to branch master
in repository x2gokdriveclient.

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

    support sending and recieving selections on demand. Support reading and writing INCR properties.
---
 client.cpp       | 299 +++++++++++++++++++++++++++---------
 client.h         |  37 +++--
 debian/changelog |   1 +
 xcbclip.cpp      | 457 ++++++++++++++++++++++++++++++++++++++++++++++---------
 xcbclip.h        |  59 ++++++-
 5 files changed, 691 insertions(+), 162 deletions(-)

diff --git a/client.cpp b/client.cpp
index e5fd37e..370b104 100644
--- a/client.cpp
+++ b/client.cpp
@@ -113,7 +113,7 @@ OutputChunk::OutputChunk(SelectionType selection, SelectionMime mime)
 {
     this->selection=selection;
     mimeData=mime;
-    compressed=false;
+    totalSize=0;
     firstChunk=lastChunk=false;
 }
 
@@ -801,9 +801,26 @@ void Client::setCursor()
 void Client::getServerversion()
 {
     serverVersion=*((uint16_t*)messageBuffer+2);
+    serverExtSelection = (serverVersion>1);
+
     qDebug()<<"server version:"<<serverVersion;
 }
 
+void Client::getClientSelection()
+{
+    uint16_t sel=*((uint16_t*)messageBuffer+2);
+    SelectionType selection=PRIMARY;
+    if(sel)
+        selection=CLIPBOARD;
+    qDebug()<<"server demands data for "<<selection;
+#ifdef Q_OS_LINUX
+    clipboard->requestSelectionData(selection);
+#else
+    sendSelectionToServer(selection);
+#endif
+
+}
+
 
 
 void Client::getCursor()
@@ -843,40 +860,72 @@ void Client::getCursor()
 void Client::getSelectionBuffer()
 {
 #ifndef Q_OS_LINUX
-#warning check it
 //if using qt class, not supporting on demand load of data
-    QClipboard::Mode mode=QClipboard::Clipboard;
-    if(!selectionClipboard)
+    setInputSelectionData(selectionClipboard, selectionFormat, firstChunk, lastChunk, compressed_size, selectionSize, messageBuffer);
+#else
+    clipboard->setInputSelectionData(selectionClipboard, selectionFormat, firstChunk, lastChunk, compressed_size, selectionSize, messageBuffer);
+#endif
+    freeMessageBuffer();
+}
+
+
+#ifndef Q_OS_LINUX
+void Client::setInputSelectionData(SelectionType, SelectionMime mime, bool firstChunk, bool lastChunk, uint32_t compressed, uint size, char* data, bool notify)
+{
+    //if notify is true, we don't have actual data, just notification
+    //copy data to selection buffer
+    //     qDebug()<<"Get chunk of input selection: selection, myme, firstChunk, lastChunk, compressed, size:"<<selection<<mime<<firstChunk<<lastChunk<<compressed<<size<<notify;
+
+
+    if(firstChunk)
     {
-        mode=QClipboard::Selection;
-        qDebug()<<"Got new Primary selection, format:"<<selectionFormat;
+        selData.clear();
+        selMime=mime;
+        total_compressed=0;
     }
+
+    if(!compressed)
+        selData.append(data,size);
     else
-        qDebug()<<"Got new Clipboard, format:"<<selectionFormat;
+    {
+        QByteArray ba;
+        ba.append((char*) &size, 4);
+        ba.append(data, compressed);
 
-    QClipboard* clipboard=QGuiApplication::clipboard();
+        total_compressed+=compressed;
 
+        selData.append(qUncompress(ba));
+        //         qDebug()<<"uncompress from "<<compressed<<" to "<<size;
+    }
 
-//     qDebug()<<messageBuffer;
-    switch(selectionFormat)
+    if(lastChunk )
     {
-        case STRING: clipboard->setText(QString::fromLocal8Bit(messageBuffer, selectionSize), mode); break;
-        case UTF_STRING: clipboard->setText(QString::fromUtf8(messageBuffer, selectionSize), mode);break;
-        case PIXMAP:
+        if(notify)
         {
-            QPixmap pix;
-            pix.loadFromData((const uchar*)messageBuffer, selectionSize);
-//             qDebug()<<pix;
-            clipboard->setImage(pix.toImage(), mode);
-            break;
+            qDebug()<<"Got selection notify from server";
+        }
+        else
+        {
+//             qDebug()<<"total size: "<<selData.size()<<"compressed size"<<total_compressed;
+            QClipboard* clipboard=QGuiApplication::clipboard();
+            QClipboard::Mode mode=QClipboard::Clipboard;
+            switch(selectionFormat)
+            {
+                case STRING: clipboard->setText(QString::fromLocal8Bit(selData), mode); break;
+                case UTF_STRING: clipboard->setText(QString::fromUtf8(selData), mode);break;
+                case PIXMAP:
+                {
+                    QPixmap pix;
+                    pix.loadFromData(selData);
+                    clipboard->setImage(pix.toImage(), mode);
+                    break;
+                }
+            }
         }
     }
-#else
-    clipboard->setInputSelectionData(selectionClipboard, selectionFormat, firstChunk, lastChunk, compressed, selectionSize, messageBuffer);
-#endif
-    freeMessageBuffer();
 }
 
+#endif
 
 void Client::getDeletedCursorsList()
 {
@@ -951,20 +1000,44 @@ void Client::getSelection()
     {
         firstChunk=*((uint32_t*)messageBuffer+4);
         lastChunk=*((uint32_t*)messageBuffer+5);
-        compressed=*((uint32_t*)messageBuffer+6);
+        compressed_size=*((uint32_t*)messageBuffer+6);
+        selectionTotalSize=*((uint32_t*)messageBuffer+7);
+        //if we are supporting selection on demand, check if it's not selection notify event
+        if(serverSupportsExtSelection() && firstChunk && lastChunk && (selectionSize == 0 ) && (selectionTotalSize == 0))
+        {
+            //set input selection with size 0 and notify true. Clipboard will know that we have a notification
+#ifdef Q_OS_LINUX
+            clipboard->setInputSelectionData(selectionClipboard, selectionFormat, true, true, 0, 0, 0, true);
+#else
+            setInputSelectionData(selectionClipboard, selectionFormat, true, true, 0, 0, 0, true);
+#endif
+        }
+        else if(serverSupportsExtSelection() && lastChunk && (selectionSize==0))
+        {
+            //it's last chunk of incr selection with size 0
+#ifdef Q_OS_LINUX
+            clipboard->setInputSelectionData(selectionClipboard, selectionFormat, firstChunk, lastChunk, 0, 0, 0);
+#else
+            setInputSelectionData(selectionClipboard, selectionFormat, firstChunk, lastChunk, 0, 0, 0);
+#endif
+        }
     }
     else
     {
         //if server doesn't support extended selection it'll send data in one chunk, uncompressed
         firstChunk=true;
         lastChunk=true;
-        compressed=false;
+        compressed_size=0;
     }
 
 
     currentDataType=SELECTIONBUFFER;
-    bytesLeftToRead=selectionSize;
-    qDebug()<<"Get Selection, is Clipboard"<<selectionClipboard<<selectionFormat<<selectionSize;
+    //if data is compressed, read the "compressed_size" bytes
+    if(compressed_size)
+        bytesLeftToRead=compressed_size;
+    else
+        bytesLeftToRead=selectionSize;
+    //     qDebug()<<"Get Selection, is Clipboard"<<selectionClipboard<<selectionFormat<<"chunk size"<<selectionSize<<"left"<<bytesLeftToRead;
     freeMessageBuffer();
 }
 
@@ -1085,7 +1158,7 @@ void Client::readDataHeader()
         }
         case SELECTION:
         {
-            qDebug()<<"Get Selection";
+//             qDebug()<<"Get Selection";
             getSelection();
             break;
         }
@@ -1094,6 +1167,11 @@ void Client::readDataHeader()
             getServerversion();
             break;
         }
+        case DEMANDCLIENTSELECTION:
+        {
+            getClientSelection();
+            break;
+        }
         default:
         {
             qDebug()<<"Unsupported header type: "<<data_type;
@@ -1364,7 +1442,7 @@ void Client::sendClientVersion()
     version=FEATURE_VERSION;
     os=OS_LINUX;
 #ifdef Q_OS_WIN
-    os=OS_WIN;
+    os=OS_WINDOWS;
 #endif
 #ifdef Q_OS_DARWIN
     os=OS_DARWIN
@@ -1377,6 +1455,19 @@ void Client::sendClientVersion()
     sendEvent(evmsg);
 }
 
+//requesting on demand selection
+void Client::requestSelectionFromServer(SelectionType sel)
+{
+    //sending the feature vesrion and OS version to the server
+    char evmsg[EVLENGTH]{};
+    uint16_t selection=sel;
+    uint32_t etype;
+    etype=DEMANDSELECTION;
+    memcpy(evmsg,(char*)&etype,4);
+    memcpy(evmsg+4,(char*)&selection,2);
+    sendEvent(evmsg);
+}
+
 
 void Client::geometryChanged()
 {
@@ -1467,6 +1558,19 @@ void Client::setUseRandr(bool use)
     }
 }
 
+void Client::send_selnotify_to_server(SelectionType selection, SelectionMime mime)
+{
+    OutputChunk* chunk=new OutputChunk(selection, mime);
+    chunk->firstChunk=chunk->lastChunk=true;
+
+    //attach empty chunk to the end of output chunk queue
+    addToSelectionOutput(chunk);
+    //send this chunk
+    sendOutputSelChunk();
+
+}
+
+
 #ifndef Q_OS_LINUX
 void Client::slotSelectionChanged(QClipboard::Mode mode)
 {
@@ -1481,42 +1585,78 @@ void Client::slotSelectionChanged(QClipboard::Mode mode)
     const QMimeData *mimeData = clipboard->mimeData(mode);
     qDebug()<<"selection changed for"<<mode<<mimeData->formats();
 
-    //if server supports ext selection only sending notification that selection is changed and the list of supported mime types
-    if(serverSupportsExtSelection())
+    SelectionType destination=PRIMARY;
+    if(mode== QClipboard::Clipboard)
+        destination=CLIPBOARD;
+
+    SelectionMime mime;
+
+
+    if(mimeData->hasImage())
+    {
+        qDebug()<<"Have new Image";
+        mime=PIXMAP;
+    }
+    else if(mimeData->hasText())
+    {
+        qDebug()<<"Have new Text";
+        mime=UTF_STRING;
+    }
+    else
     {
+        qDebug()<<"Unsupported MIME type in clipboard";
+        return;
+    }
 
+    if(serverSupportsExtSelection())
+    {
+        //send notify to server
+        send_selnotify_to_server(destination,mime);
     }
     else
     {
-        //server doesn't support ext selection we are sending clipboard data
+        //send data to server
+        sendSelectionToServer(destination);
     }
 
-    QByteArray data;
-    //add size/mime/type of buffer and start copy data
-    char evmsg[EVLENGTH]{};
+}
 
-    uint32_t etype=SELECTIONEVENT;
-    uint32_t size;
-    uint8_t destination=PRIMARY;
-    if(mode== QClipboard::Clipboard)
-        destination=CLIPBOARD;
+void Client::sendSelectionToServer(SelectionType selection)
+{
+    //sending selection data to server
+    if(!connected)
+        return;
+    const QClipboard *clipboard = QGuiApplication::clipboard();
+
+    QClipboard::Mode mode;
+    if(selection==PRIMARY)
+        mode= QClipboard::Selection;
+    else
+        mode =QClipboard::Clipboard;
+
+    if(mode == QClipboard::Clipboard && clipboard->ownsClipboard())
+        return;
+    if(mode == QClipboard::Selection && clipboard->ownsSelection())
+        return;
 
-    uint8_t mime;
+    const QMimeData *mimeData = clipboard->mimeData(mode);
+    SelectionMime mime;
 
+    QByteArray data;
 
     if(mimeData->hasImage())
     {
         QBuffer buffer(&data);
         buffer.open(QIODevice::WriteOnly);
         QPixmap pix=clipboard->pixmap(mode);
-        pix.save(&buffer, "JPG");
-        qDebug()<<"Have new Image:"<<pix<<data.size();
+        pix.save(&buffer, "PNG");
+        qDebug()<<"Selection image size"<<pix<<data.size();
         mime=PIXMAP;
     }
     else if(mimeData->hasText())
     {
         data=clipboard->text(mode).toUtf8();
-        qDebug()<<"Have new Text:"<<data.size();
+        qDebug()<<"Selection Text"<<data.size();
         mime=UTF_STRING;
     }
     else
@@ -1524,30 +1664,23 @@ void Client::slotSelectionChanged(QClipboard::Mode mode)
         qDebug()<<"Unsupported MIME type in clipboard";
         return;
     }
-    size=data.size();
-    if(!size)
+
+    if(!data.size())
     {
-        qDebug()<<"Clipboard has no data";
+        qDebug()<<"no data";
         return;
     }
 
-    memcpy(evmsg,(char*)&etype,4);
-    memcpy(evmsg+4,(char*)&size,4);
-    memcpy(evmsg+8,(char*)&destination,1);
-    memcpy(evmsg+9,(char*)&mime,1);
-
-    qDebug()<<"SEND SELECTION"<<size<<destination<<mime;
-    uint32_t sentData=(size < EVLENGTH-10)?size:EVLENGTH-10;
-    memcpy(evmsg+10,data.data(),sentData);
-    sendEvent(evmsg);
-    while(sentData<size)
-    {
-        int chunk=(size-sentData < EVLENGTH)?size-sentData:EVLENGTH;
-        memcpy(evmsg, data.data()+sentData, chunk);
-        sentData+=chunk;
-        sendEvent(evmsg);
-    }
-    qDebug()<<"sent: "<<sentData<<"from"<<size;
+    OutputChunk* chunk;
+    chunk=new OutputChunk(selection, mime);
+    chunk->totalSize=data.size();
+    chunk->data=data;
+    chunk->mimeData=mime;
+    chunk->selection=selection;
+    chunk->firstChunk=true;
+    chunk->lastChunk=true;
+    addToSelectionOutput(chunk);
+    sendOutputSelChunk();
 }
 #endif
 
@@ -1558,7 +1691,7 @@ void Client::sendOutputSelChunk()
     if(outputSelectionQueue.isEmpty())
         return;
 
-    OutputChunk* chunk=outputSelectionQueue.takeLast();
+    OutputChunk* chunk=outputSelectionQueue.takeFirst();
     if(!serverSupportsExtSelection() && (!chunk->firstChunk || !chunk->lastChunk))
     {
         //selection has multiply chunks, but this server doesn't support ext selection, not sending anything
@@ -1574,7 +1707,7 @@ void Client::sendOutputSelChunk()
     uint8_t mime=chunk->mimeData;
     uint8_t firstChunk=chunk->firstChunk;
     uint8_t lastChunk=chunk->lastChunk;
-    uint8_t compressed=chunk->compressed;
+    uint32_t compressed_size=0;
     uint32_t totalSize=chunk->totalSize;
 
     size=chunk->data.size();
@@ -1587,24 +1720,41 @@ void Client::sendOutputSelChunk()
     //if server supports extended selection, sending extended header
     if(serverSupportsExtSelection())
     {
+        //if server supports it compress the big string data
+        if(chunk->mimeData==UTF_STRING && serverSupportsExtSelection() && size >1024)
+        {
+            chunk->data=qCompress(chunk->data);
+            //Qt puting uncompressed size of data in the first 4 bytes of buffer, we won't send them
+            compressed_size=chunk->data.size()-4;
+        }
         memcpy(evmsg+10,(char*)&firstChunk,1);
         memcpy(evmsg+11,(char*)&lastChunk,1);
-        memcpy(evmsg+12,(char*)&compressed,1);
-        memcpy(evmsg+13,(char*)&totalSize,4);
+        memcpy(evmsg+12,(char*)&compressed_size,4);
+        memcpy(evmsg+16,(char*)&totalSize,4);
+//         qDebug()<<"size of chunk: "<<size<<" compressed: "<<compressed_size<<"total: "<<totalSize;
     }
 
     uint headerSize=10;
     if(serverSupportsExtSelection())
-        headerSize=17;
+        headerSize=20;
 
 //     qDebug()<<"SEND SELECTION"<<size<<destination<<mime;
+    char* data_ptr=chunk->data.data();
+
+    if(compressed_size)
+    {
+        //sending data compressed
+        data_ptr+=4;//don't send first 4 bytes
+        size=compressed_size;
+    }
+
     uint32_t sentData=(size < EVLENGTH-headerSize)?size:EVLENGTH-headerSize;
-    memcpy(evmsg+headerSize,chunk->data.data(),sentData);
+    memcpy(evmsg+headerSize,data_ptr,sentData);
     sendEvent(evmsg);
     while(sentData<size)
     {
         int msg_length=(size-sentData < EVLENGTH)?size-sentData:EVLENGTH;
-        memcpy(evmsg, chunk->data.data()+sentData, msg_length);
+        memcpy(evmsg, data_ptr+sentData, msg_length);
         sentData+=msg_length;
         sendEvent(evmsg);
     }
@@ -1616,3 +1766,12 @@ void Client::addToSelectionOutput(OutputChunk* chunk)
 {
     outputSelectionQueue.append(chunk);
 }
+
+int Client::max_chunk()
+{
+    if(serverSupportsExtSelection())
+    {
+        return 1024*256/4; //256KB
+    }
+    return 10*1024*1024/4; //10MB
+}
diff --git a/client.h b/client.h
index 52d1cbf..a947653 100644
--- a/client.h
+++ b/client.h
@@ -24,8 +24,8 @@
 
 //FEATURE_VERSION is not cooresponding to actual version of client
 //it used to tell server which features are supported by client
-//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
 
 //Version of client OS for same reason
 enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
@@ -58,6 +58,7 @@ enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
 #define UPDATE 8
 #define SELECTIONEVENT 9
 #define CLIENTVERSION 10
+#define DEMANDSELECTION 11
 
 #define ShiftMask (1<<0)
 #define LockMask (1<<1)
@@ -134,10 +135,9 @@ public:
     OutputChunk(SelectionType selection, SelectionMime mimeType);
     QByteArray data;
     enum SelectionMime mimeData;
-    bool compressed;
-    bool firstChunk;
-    bool lastChunk;
-    uint totalSize;
+    bool firstChunk=false;
+    bool lastChunk=false;
+    uint32_t totalSize=0;
     enum SelectionType selection;
 };
 
@@ -167,6 +167,11 @@ public:
     void addToSelectionOutput(OutputChunk* chunk);
     bool serverSupportsExtSelection(){return serverExtSelection;}
     ClipboardMode clipboardMode(){return clipMode;}
+    void requestSelectionFromServer(SelectionType sel);
+    void send_selnotify_to_server(SelectionType selection, SelectionMime mime);
+    int max_chunk();
+
+public slots:
     void sendOutputSelChunk();
 
 
@@ -203,9 +208,10 @@ public slots:
 
 private:
     enum{ HEADER, FRAMEREGION, REGIONDATA ,CURSORDATA, CURSORLIST, FRAMELIST, SELECTIONBUFFER } currentDataType;
-    enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, SERVER_VERSION};
+    enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, SERVER_VERSION, DEMANDCLIENTSELECTION};
 
     void getServerversion();
+    void getClientSelection();
     void setUseRandr(bool use);
     void exitOnError(const QString& message);
     void getImageFrame();
@@ -226,6 +232,10 @@ private:
     void sendGeometryEvent();
     void setFS(int screenNumber);
     bool wantRepaint=false;
+#ifndef Q_OS_LINUX
+    void sendSelectionToServer(SelectionType selection);
+    void setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, uint32_t compressed, uint size, char* data, bool notify=false);
+#endif
 
 
     //initial values
@@ -233,7 +243,7 @@ private:
     int height=600;
 
     //feature version of server
-    u_int16_t serverVersion=0;
+    quint16 serverVersion=0;
 
     bool fullscreen=false;
     bool multidisp=false;
@@ -273,6 +283,8 @@ private:
     //input selection chunk variables
     //size of current chunk
     uint32_t selectionSize;
+    //size of complete selection
+    uint32_t selectionTotalSize;
     //format of chunk, string or pix
     SelectionMime selectionFormat;
     //if true clipboard else primary
@@ -281,8 +293,8 @@ private:
     bool firstChunk;
     //if it's the last chunk in multiply sel
     bool lastChunk;
-    //if the chunk compressed
-    bool compressed;
+    //if the chunk compressed the size is > 0
+    uint32_t compressed_size;
     //////////
 
     ClipboardMode clipMode=CLIP_BOTH; //clipboard mode: both, server, client or none
@@ -306,8 +318,13 @@ private:
     ScreenIdentifier *screenIdentifier=0l;
     QLabel* fr;
 
+    //selection
 #ifdef Q_OS_LINUX
     XCBClip* clipboard;
+#else
+    SelectionMime selMime; //mime of selection (string or image)
+    QByteArray selData; //data
+    uint32_t total_compressed=0;
 #endif
 
 protected:
diff --git a/debian/changelog b/debian/changelog
index 84f52c2..9f8af45 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -25,5 +25,6 @@ x2gokdriveclient (0.0.0.1-0x2go1) UNRELEASED; urgency=medium
     - add command line argument --selection (both|none|server|client) to specify selection mode.
     - send a recive feature versions.
     - give server some time for initialization before sending version.
+    - support sending and recieving selections on demand. Support reading and writing INCR properties.
 
  -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Tue, 04 Jun 2019 11:10:43 +0200
diff --git a/xcbclip.cpp b/xcbclip.cpp
index e62d311..34cf6c2 100644
--- a/xcbclip.cpp
+++ b/xcbclip.cpp
@@ -23,8 +23,12 @@
 #include <QX11Info>
 #include <QTimer>
 #include <QBuffer>
+#include <QDateTime>
 
 
+#define SELECTION_DELAY 30000 //timeout for selection operation
+#define INCR_SIZE 256*1024 //size of part for incr selection incr selection
+
 XCBClip::XCBClip(Client* parent)
 {
 
@@ -46,6 +50,8 @@ XCBClip::XCBClip(Client* parent)
     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 (con,
                        XCB_COPY_FROM_PARENT,
@@ -76,7 +82,7 @@ XCBClip::XCBClip(Client* parent)
             //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(con,clipWinId, XCB_ATOM_PRIMARY, mask);
-            xcb_xfixes_select_selection_input_checked(con, clipWinId, atom("CLIPBOARD"), mask);
+            xcb_xfixes_select_selection_input_checked(con, clipWinId, ATOM_CLIPBOARD, mask);
         }
         free(xfixes_query);
     }
@@ -84,6 +90,18 @@ XCBClip::XCBClip(Client* parent)
     QTimer::singleShot(250, this, SLOT(checkEvents()));
 }
 
+XCBClip::~XCBClip()
+{
+    for(uint i = delayedSelectionRequests.length()-1; i<=0;--i)
+    {
+        discardDelayedRequest(i);
+    }
+    //remove all pending INCR requests
+    remove_obsolete_incr_transactions(false);
+    xcb_destroy_window(con, clipWinId);
+    xcb_disconnect(con);
+}
+
 
 xcb_atom_t XCBClip::atom(const QString& name)
 {
@@ -137,6 +155,7 @@ QString XCBClip::atom_name(xcb_atom_t xatom)
     xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(con, xatom);
     xcb_get_atom_name_reply_t *reply=xcb_get_atom_name_reply(con, cookie, NULL);
 
+
     if(!reply)
         return name;
     if(!reply->name_len)
@@ -228,8 +247,88 @@ xcb_atom_t XCBClip::target_has_atom(const QStringList& atoms, const QString& nam
     return 0;
 }
 
+void XCBClip::discardDelayedRequest(uint index)
+{
+    DelayedRequest *d=delayedSelectionRequests.takeAt(index);
+    xcb_send_event(con, false, d->request->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)d->event);
+    xcb_flush(con);
+    free(d->event);
+    free((xcb_generic_event_t *)(d->request));
+    delete d;
+}
+
+
+void XCBClip::processDelayedRequests()
+{
+    //process delayed requests
+    for(uint i = delayedSelectionRequests.length()-1; i<=0;--i)
+    {
+        DelayedRequest* d=delayedSelectionRequests[i];
+        SelectionType selection = selection_from_atom( d->request->selection);
+        if(currentXTime() > (d->request->time + SELECTION_DELAY))
+        {
+//             qDebug()<<"timeout selection: "<<selection;
+            discardDelayedRequest(i);
+            continue;
+        }
+        if(!inputSelection[selection].owner)
+        {
+//             qDebug()<<"we are not owner of requested selection: "<<selection;
+            //we are not anymore owners of this selection
+            discardDelayedRequest(i);
+            continue;
+        }
+        if(inputSelection[selection].timestamp > d->request->time )
+        {
+//             qDebug()<<"selection request for "<<selection<<" is too old";
+            //requested selection is older than the current one
+            discardDelayedRequest(i);
+            continue;
+        }
+        if(!check_req_sanity(d->request))
+        {
+//             qDebug()<<"can't convert selection "<<selection<<" to requested myme type "<<d->request->property;
+            //our selection don't support requested mime type
+            discardDelayedRequest(i);
+            continue;
+        }
+        if(inputSelection[selection].state != InputSelection::COMPLETED)
+        {
+            //we don't have the data yet
+            continue;
+        }
+        d->event->property=send_data(d->request);
+        discardDelayedRequest(i);
+    }
+}
+
+
+void XCBClip::updateCurrentTime(xcb_timestamp_t t)
+{
+    //updating the current X time. It's not very precicely, but enough for us
+    if(t > lastXTime)
+    {
+        //update current time
+        lastXTime=t;
+        timeDifference=QDateTime::currentMSecsSinceEpoch() - t;
+//         qDebug()<<"X time dif:"<<QDateTime::currentMSecsSinceEpoch() - t<<"x11 time"<<t<<"calculated: "<<currentXTime();
+    }
+}
+
+xcb_timestamp_t XCBClip::currentXTime()
+{
+    //get current X time
+    return QDateTime::currentMSecsSinceEpoch() - timeDifference;
+
+}
+
+
 void XCBClip::checkEvents()
 {
+    //check delayed events
+    processDelayedRequests();
+    //delete obsolete INCR transactions if we have something
+    remove_obsolete_incr_transactions();
     xcb_generic_event_t *e=xcb_poll_for_event(con);
     if(!e)
     {
@@ -244,6 +343,9 @@ void XCBClip::checkEvents()
     if (response_type == reply->first_event + XCB_XFIXES_SELECTION_NOTIFY)
     {
         xcb_xfixes_selection_notify_event_t *notify_event=(xcb_xfixes_selection_notify_event_t *)e;
+        updateCurrentTime(notify_event->timestamp);
+
+
 //         qDebug()<<"SEL OWNER notify, selection:"<<notify_event->selection<< " window "<< notify_event->window<< "owner"<< notify_event->owner;
         if(notify_event->owner == clipWinId)
         {
@@ -265,11 +367,11 @@ void XCBClip::checkEvents()
             incrAtom=0;
             if(notify_event->selection==XCB_ATOM_PRIMARY)
             {
-                owner[0]=false;
+                inputSelection[PRIMARY].owner=false;
             }
             else
             {
-                owner[1]=false;
+                inputSelection[CLIPBOARD].owner=false;
             }
             //get supported mime types
             request_selection_data( notify_event->selection, atom( "TARGETS"), atom( "TARGETS"), 0);
@@ -289,7 +391,13 @@ void XCBClip::checkEvents()
         }
         else if (response_type == XCB_SELECTION_REQUEST)
         {
-            process_selection_request(e);
+            if(!process_selection_request(e))
+            {
+                //we delayed processing of this request till data received from server
+                //we will free the event when we have the data
+                QTimer::singleShot(10, this, SLOT(checkEvents()));
+                return;
+            }
         }
         else
         {
@@ -312,6 +420,9 @@ void XCBClip::process_selection_notify(xcb_generic_event_t *e)
 
 //     qDebug()<<"selection notify";
     sel_event=(xcb_selection_notify_event_t *)e;
+    updateCurrentTime(sel_event->time);
+
+
 
     //processing the event which is reply for convert selection call
 
@@ -326,7 +437,7 @@ void XCBClip::process_selection_notify(xcb_generic_event_t *e)
 //         qDebug()<<"selection notify sel , target , property "<< sel_event->selection<< sel_event->target<< sel_event->property;
         if(sel_event->property==XCB_NONE)
         {
-            qDebug()<<( "NO SELECTION");
+//             qDebug()<<( "NO SELECTION");
         }
         else
         {
@@ -338,6 +449,70 @@ void XCBClip::process_selection_notify(xcb_generic_event_t *e)
     }
 }
 
+void XCBClip::destroy_incr_transaction(int index)
+{
+    //destroy incr transaction with index
+    IncrTransaction* tr=incrTransactions.takeAt(index);
+    const quint32 mask[] = { XCB_EVENT_MASK_NO_EVENT };
+    //don't resive property notify events for this window anymore
+    xcb_change_window_attributes(con, tr->requestor,
+                                 XCB_CW_EVENT_MASK, mask);
+    xcb_flush(con);
+
+    delete tr;
+}
+
+
+void XCBClip::remove_obsolete_incr_transactions( bool checkTs)
+{
+    //remove_obsolete_incr_transactions
+    //if checkTS true, check timestamp and destroy only if ts exceed delay
+    for (int i= incrTransactions.size()-1;i>=0;--i)
+    {
+        if( (!checkTs) || (  incrTransactions[i]->timestamp+SELECTION_DELAY  <  QDateTime::currentMSecsSinceEpoch()))
+        {
+            qDebug()<<"timeout INCR selection for "<<incrTransactions[i]->requestor<<incrTransactions[i]->timestamp<< QDateTime::currentMSecsSinceEpoch();
+            destroy_incr_transaction(i);
+        }
+    }
+}
+
+
+void XCBClip::process_incr_transaction_property(xcb_property_notify_event_t * pn)
+{
+    //process incr transactions
+    for (int i=0;i < incrTransactions.size();++i)
+    {
+        IncrTransaction* tr=incrTransactions[i];
+        if((tr->requestor == pn->window) && (tr->property == pn->atom ) && ( pn->state == XCB_PROPERTY_DELETE) )
+        {
+            //requestor ready for the new portion of data
+            uint left=tr->data.size()-tr->sentBytes;
+            if(!left)
+            {
+//                 qDebug()<<"all INCR data sent to"<<tr->requestor;
+                //all data sent, sending NULL data and destroying transaction
+                xcb_change_property(con, XCB_PROP_MODE_REPLACE, tr->requestor, tr->property,
+                                    tr->target, 8, 0, NULL);
+                xcb_flush(con);
+                destroy_incr_transaction(i);
+                return;
+            }
+            uint sendingBytes=(INCR_SIZE< left)?INCR_SIZE:left;
+
+//             qDebug()<<"sending incr bytes"<<sendingBytes ;
+
+            xcb_change_property(con, XCB_PROP_MODE_REPLACE, tr->requestor, tr->property,
+                                tr->target, 8, sendingBytes, tr->data.constData() + tr->sentBytes);
+            xcb_flush(con);
+            tr->sentBytes+=sendingBytes;
+            tr->timestamp=QDateTime::currentMSecsSinceEpoch();
+            return;
+        }
+    }
+    //notify event doesn't belong to any of started incr transactions or it's notification for new property
+    return;
+}
 
 void XCBClip::process_property_notify(xcb_generic_event_t *e)
 {
@@ -346,9 +521,13 @@ void XCBClip::process_property_notify(xcb_generic_event_t *e)
 //     qDebug()<<("property notify");
 
     pn = (xcb_property_notify_event_t *)e;
+    updateCurrentTime(pn->time);
+
     if (pn->window != clipWinId)
     {
-//         qDebug()<<("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;
     }
 //     qDebug()<<"property, state "<< pn->atom<< pn->state;
@@ -375,7 +554,7 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
 
     //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(con,false, clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, 0, max_chunk());
+    cookie= xcb_get_property(con,false, clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, 0, parent->max_chunk());
     reply=xcb_get_property_reply(con, cookie, NULL);
     if(!reply)
     {
@@ -385,7 +564,7 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
     {
         if(reply->type==XCB_NONE)
         {
-            qDebug()<<( "NONE reply");
+//             qDebug()<<( "NONE reply");
         }
         else
         {
@@ -420,17 +599,27 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                 {
                     QStringList atoms=atomsInReply(reply);
 //                     qDebug() << "target supports mime types:"<<atoms;
-
+                    data_atom=0;
+                    //get the best of supported mime types and request the selection in this format
+                    data_atom=best_atom_from_list(atoms);
                     if(parent->serverSupportsExtSelection())
                     {
                         //servere support extended selection, we'll send the selection data first when it's requested by the server
                         //now we'll just notify the server that there is the new selection and send supported mime types
+                        SelectionType sel=selection_from_atom(selection);
+                        best_atom[sel]=data_atom;
+                        SelectionMime mime=UTF_STRING;
+
+                        if( ! is_string_atom(best_atom[sel]) )
+                        {
+                            mime = PIXMAP;
+                        }
+
+//                         qDebug()<<"MIME"<<mime;
+                        parent->send_selnotify_to_server(sel,mime);
                     }
                     else
                     {
-                        data_atom=0;
-                        //get the best of supported mime types and request the selection in this format
-                        data_atom=best_atom_from_list(atoms);
 
                         xcb_delete_property( con, clipWinId, property);
                         xcb_flush(con);
@@ -440,7 +629,7 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                             request_selection_data( selection, data_atom, data_atom, 0);
                         else
                         {
-                            qDebug()<<( "there are no supported mime types in the target");
+//                             qDebug()<<( "there are no supported mime types in the target");
                         }
                     }
                 }
@@ -462,9 +651,7 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
 
 
 
-                        SelectionType sel= CLIPBOARD;
-                        if(selection==XCB_ATOM_PRIMARY)
-                            sel=PRIMARY;
+                        SelectionType sel= selection_from_atom(selection);
 
                         SelectionMime mime= UTF_STRING;
                         if(is_image_atom(reply->type))
@@ -478,18 +665,14 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                             chunk->data.setRawData((const char*) xcb_get_property_value(reply),xcb_get_property_value_length(reply));
                         }
 
-                        chunk->compressed=false;
                         if(is_string_atom(property))
+                        {
                             chunk->mimeData=UTF_STRING;
+                        }
                         else
                             chunk->mimeData=PIXMAP;
 
-                        if(selection == XCB_ATOM_PRIMARY)
-                        {
-                            chunk->selection=PRIMARY;
-                        }
-                        else
-                            chunk->selection=CLIPBOARD;
+                        chunk->selection=selection_from_atom(selection);
 
 
                         if(incrementalSize && (incrAtom==property))
@@ -540,7 +723,7 @@ void XCBClip::read_selection_property(xcb_atom_t selection, xcb_atom_t property)
                         if(bytes_left)
                         {
                             free(reply);
-                            cookie= xcb_get_property(con, 0, clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, bytes_read/4,max_chunk());
+                            cookie= xcb_get_property(con, 0, clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, bytes_read/4,parent->max_chunk());
                             reply=xcb_get_property_reply(con, cookie, NULL);
                             if(!reply)
                             {
@@ -575,6 +758,21 @@ SelectionType XCBClip::selection_from_atom(xcb_atom_t selection)
 
 }
 
+xcb_atom_t XCBClip::atom_from_selection(SelectionType selection)
+{
+    if(selection == PRIMARY)
+        return XCB_ATOM_PRIMARY;
+    return ATOM_CLIPBOARD;
+
+}
+
+void XCBClip::requestSelectionData(SelectionType selection)
+{
+    //Client requesting data for selection using this function
+    request_selection_data(atom_from_selection(selection), best_atom[selection], best_atom[selection], 0);
+}
+
+
 void  XCBClip::request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t)
 {
     //execute convert selection for primary or clipboard to get mimetypes or data (depends on target atom)
@@ -589,23 +787,53 @@ void  XCBClip::request_selection_data( xcb_atom_t selection, xcb_atom_t target,
 }
 
 
-void XCBClip::setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, bool compressed, uint size, char* data)
+void XCBClip::setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, uint32_t compressed, uint size, char* data, bool notify)
 {
+    //if notify is true, we don't have actual data, just notification
     //copy data to selection buffer
-//     qDebug()<<"Get chunk of input selection: selection, myme, firstChunk, lastChunk, compressed, size:"<<selection<<mime<<firstChunk<<lastChunk<<compressed<<size;
+//     qDebug()<<"Get chunk of input selection: selection, myme, firstChunk, lastChunk, compressed, size:"<<selection<<mime<<firstChunk<<lastChunk<<compressed<<size<<notify;
 
 
     if(firstChunk)
     {
-        selData[selection].clear();
-        selMime[selection]=mime;
+        inputSelection[selection].selData.clear();
+        inputSelection[selection].selMime=mime;
+        total_compressed=0;
     }
 
-    selData[selection].append(data,size);
+    if(!compressed)
+        inputSelection[selection].selData.append(data,size);
+    else
+    {
+        QByteArray ba;
+        ba.append((char*) &size, 4);
+        ba.append(data, compressed);
+
+        total_compressed+=compressed;
+
+        inputSelection[selection].selData.append(qUncompress(ba));
+//         qDebug()<<"uncompress from "<<compressed<<" to "<<size;
+    }
 
-    if(lastChunk)
+    if(lastChunk )
     {
-        own_selection(selection);
+        if(notify)
+        {
+            inputSelection[selection].state=InputSelection::NOTIFIED;
+//             qDebug()<<"Got selection notify from server";
+            own_selection(selection);
+        }
+        else
+        {
+            //if state is requested, means we already own a selection
+            if(inputSelection[selection].state!=InputSelection::REQUESTED)
+            {
+                own_selection(selection);
+            }
+            inputSelection[selection].state=InputSelection::COMPLETED;
+//             qDebug()<<"Got selection data for "<<selection<<"total size: "<<inputSelection[selection].selData.size()<<"compressed size"<<total_compressed;
+        }
+
     }
 }
 
@@ -620,21 +848,25 @@ void XCBClip::own_selection(SelectionType selection)
         default:
             break;
     }
-    xcb_atom_t sel=XCB_ATOM_PRIMARY;
-    if(selection!=PRIMARY)
-    {
-        sel=atom("CLIPBOARD");
-    }
+    xcb_atom_t sel=atom_from_selection(selection);
     xcb_set_selection_owner(con, clipWinId, sel, XCB_CURRENT_TIME);
     xcb_flush(con);
-    owner[selection]=true;
-    timestamp[selection]=XCB_CURRENT_TIME;
+    inputSelection[selection].owner=true;
+    inputSelection[selection].timestamp=currentXTime();
 }
 
-void XCBClip::process_selection_request(xcb_generic_event_t *e)
+bool XCBClip::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;
 
+    updateCurrentTime(req->time);
+
     xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
     event->response_type = XCB_SELECTION_NOTIFY;
     event->requestor = req->requestor;
@@ -651,25 +883,25 @@ void XCBClip::process_selection_request(xcb_generic_event_t *e)
 
     SelectionType sel=selection_from_atom(req->selection);
 
-//     qDebug()<<"selection request for"<<atom_name(req->selection)<<atom_name(req->target)<<atom_name(req->property);
-    if(!owner[sel])
+//     qDebug()<<"selection request for"<<atom_name(req->selection)<<atom_name(req->target)<<atom_name(req->property)<< "from "<<req->requestor<<"we are "<<clipWinId;
+    if(!inputSelection[sel].owner)
     {
         //we don't own this selection
-        qDebug()<<"not our selection";
+//         qDebug()<<"not our selection";
         xcb_send_event(con, false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
         xcb_flush(con);
         free(event);
-        return;
+        return true;
     }
 
-    if(timestamp[sel] > req->time)
+    if(inputSelection[sel].timestamp > req->time)
     {
         //selection changed after request
-        qDebug()<<"requested selection doesn't exist anymore";
+//         qDebug()<<"requested selection doesn't exist anymore";
         xcb_send_event(con, false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
         xcb_flush(con);
         free(event);
-        return;
+        return true;
     }
 
 
@@ -678,7 +910,7 @@ void XCBClip::process_selection_request(xcb_generic_event_t *e)
         event->property=property;
 //         qDebug()<<"requested TIMESTAMP";
         xcb_change_property(con, XCB_PROP_MODE_REPLACE, req->requestor,
-                                property, XCB_ATOM_INTEGER, 32, 1, &timestamp[sel]);
+                            property, XCB_ATOM_INTEGER, 32, 1, &inputSelection[sel].timestamp);
 
     }
     else if(req->target==atom("TARGETS"))
@@ -689,11 +921,39 @@ void XCBClip::process_selection_request(xcb_generic_event_t *e)
     }
     else
     {
-        event->property=send_data(req);
+        if(check_req_sanity(req))
+        {
+            //if data is ready, send it to requestor
+            if(inputSelection[sel].state == InputSelection::COMPLETED)
+                event->property=send_data(req);
+            else
+            {
+                //if data is not ready, request it from server and delay the processing of request
+                delay_selection_request(req, event);
+                return false;
+            }
+        }
     }
     xcb_send_event(con, false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
     xcb_flush(con);
     free(event);
+    return true;
+}
+
+void XCBClip::delay_selection_request( xcb_selection_request_event_t *request, xcb_selection_notify_event_t* event)
+{
+    SelectionType sel=selection_from_atom(request->selection);
+    DelayedRequest* dr=new DelayedRequest;
+    dr->event=event;
+    dr->request=request;
+    //add new request to the queue
+    delayedSelectionRequests<<dr;
+    if(inputSelection[sel].state==InputSelection::NOTIFIED)
+    {
+        //if we didn't request the data yet, let's do it now
+        parent->requestSelectionFromServer(sel);
+        inputSelection[sel].state=InputSelection::REQUESTED;
+    }
 }
 
 QString XCBClip::mime_to_QT_img(const QString& mimeType)
@@ -710,54 +970,91 @@ QString XCBClip::mime_to_QT_img(const QString& mimeType)
 
 xcb_atom_t XCBClip::set_data_property(xcb_selection_request_event_t* req, QByteArray* data)
 {
-
     //set data to window property
 
     //change when implemented
-    bool support_incr=false;
+    bool support_incr=true;
     //this types of application not supporting incr selection
     if(atom_name(req->property)=="_XT_SELECTION_0" || atom_name(req->property)=="_QT_SELECTION")
     {
-        qDebug()<<atom_name(req->property)<<"doesn't support INCR";
+//         qDebug()<<atom_name(req->property)<<"doesn't support INCR";
         support_incr=false;
     }
-//     qDebug()<<"check if data size < "<<xcb_get_maximum_request_length(con) * 4 - 24;
 
     //check if we are sending incr
-    if(data->size() < (int)xcb_get_maximum_request_length(con) * 4 - 24)
+
+    if(!support_incr)
     {
-//         qDebug()<<"sending "<<data->size()<<atom_name(req->property)<<atom_name(req->target);
+        if( data->size() < (int)xcb_get_maximum_request_length(con) * 4 - 24)
+        {
+            //requester doesn't support INCR, sending data in one chunk
+            xcb_change_property(con, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
+                                8, data->size(), (const void *)data->constData());
+
+            xcb_flush(con);
+            return req->property;
+        }
+        qDebug()<<"data is too big";
+        return XCB_NONE;
+    }
 
+    if(data->size() < INCR_SIZE)
+    {
+        //if size is < 256K send in one property
         xcb_change_property(con, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
                             8, data->size(), (const void *)data->constData());
-
         xcb_flush(con);
         return req->property;
     }
 
-    qDebug()<<"data is too big";
-    return XCB_NONE;
+    //sending INCR atom to let requester know that we are starting data incrementally
+    uint bytes=data->size();
+    qDebug()<<"starting INCR send of size"<<data->size()<<" for win ID "<<req->requestor;
+    xcb_change_property(con, XCB_PROP_MODE_REPLACE, req->requestor, req->property,
+                        atom("INCR"), 32, 1, (const void *)&bytes);
+    startIncrTransaction(req->requestor, req->property, req->target, *data );
+    xcb_flush(con);
+
+    return req->property;
 }
 
 
-xcb_atom_t XCBClip::send_data(xcb_selection_request_event_t* req)
+
+//creating INCR transaction
+void XCBClip::startIncrTransaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, QByteArray data)
 {
+    IncrTransaction* tr=new IncrTransaction;
+    tr->requestor=requestor;
+    tr->property=property;
+    tr->target=target;
+    tr->data=data;
+    tr->sentBytes=0;
+    tr->timestamp=QDateTime::currentMSecsSinceEpoch();
+    incrTransactions<<tr;
+    qDebug()<<"INCR start"<<tr->timestamp;
+    const quint32 mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+    //we'll recive property change events for requestor window from now
+    xcb_change_window_attributes(con, requestor,
+                                 XCB_CW_EVENT_MASK, mask);
+}
 
-    //send data
-    SelectionType sel=selection_from_atom(req->selection);
 
-    if(selMime[sel]==UTF_STRING)
+//check if the requested mime can be delivered
+bool XCBClip::check_req_sanity(xcb_selection_request_event_t* req)
+{
+    SelectionType sel=selection_from_atom(req->selection);
+    if(inputSelection[sel].selMime==UTF_STRING)
     {
         //if it's one of supported text formats send without convertion
         if(is_string_atom(req->target))
         {
-//             qDebug()<<"sending UTF text";
-            return set_data_property(req, &selData[sel]);
+            //             qDebug()<<"sending UTF text";
+            return true;
         }
         else
         {
             qDebug()<<"unsupported property requested:"<<atom_name(req->target);
-            return XCB_NONE;
+            return false;
         }
     }
     else
@@ -765,11 +1062,28 @@ xcb_atom_t XCBClip::send_data(xcb_selection_request_event_t* req)
         if(!is_image_atom(req->target))
         {
             qDebug()<<"unsupported property requested:"<<atom_name(req->target);
-            return XCB_NONE;
+            return false;
         }
+        //         qDebug()<<"sending "<<atom_name(req->target);
+        return true;
+    }
+}
+
+
+xcb_atom_t XCBClip::send_data(xcb_selection_request_event_t* req)
+{
+    //send data
+    SelectionType sel=selection_from_atom(req->selection);
+    if(inputSelection[sel].selMime==UTF_STRING)
+    {
+        //if it's one of supported text formats send without convertion
+        return set_data_property(req, &inputSelection[sel].selData);
+    }
+    else
+    {
 //         qDebug()<<"sending "<<atom_name(req->target);
         //convert to desireable format
-        QImage img=QImage::fromData(selData[sel]);
+        QImage img=QImage::fromData(inputSelection[sel].selData);
         QByteArray ba;
         QBuffer buffer(&ba);
         buffer.open(QIODevice::WriteOnly);
@@ -777,7 +1091,6 @@ xcb_atom_t XCBClip::send_data(xcb_selection_request_event_t* req)
 //         qDebug()<<"converted to"<<mime_to_QT_img(atom_name(req->target))<<ba.size();
         return set_data_property(req, &ba);
     }
-    return XCB_NONE;
 }
 
 void XCBClip::send_mime_types(xcb_selection_request_event_t* req)
@@ -792,7 +1105,7 @@ void XCBClip::send_mime_types(xcb_selection_request_event_t* req)
     if((a=atom("TIMESTAMP")))
         targets.append(a);
 
-    if(selMime[sel]==PIXMAP)
+    if(inputSelection[sel].selMime==PIXMAP)
     {
         if((a=atom("image/png")))
             targets.append(a);
@@ -825,10 +1138,4 @@ void XCBClip::send_mime_types(xcb_selection_request_event_t* req)
 
 }
 
-uint XCBClip::max_chunk()
-{
-    if(parent->serverSupportsExtSelection())
-        return 1024*100/4; //100KB
-        else
-            return 10*1024*1024/4; //10MB
-}
+
diff --git a/xcbclip.h b/xcbclip.h
index c45fc91..7787e50 100644
--- a/xcbclip.h
+++ b/xcbclip.h
@@ -34,17 +34,18 @@ class XCBClip : public QObject
     Q_OBJECT
 public:
     XCBClip(Client* parent);
-    void setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, bool compressed, uint size, char* data);
+    ~XCBClip();
+    void setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, uint32_t compressed, uint size, char* data, bool notify=false);
+    void requestSelectionData(SelectionType selection);
 
 private:
 
-    uint max_chunk(void);
     xcb_atom_t atom(const QString& name);
     QString atom_name(xcb_atom_t xatom);
     QStringList atomsInReply(xcb_get_property_reply_t *reply);
     xcb_atom_t best_atom_from_list(const QStringList& atoms);
     void process_selection_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 request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t);
     void process_property_notify(xcb_generic_event_t *e);
     void read_selection_property(xcb_atom_t selection, xcb_atom_t property);
@@ -56,7 +57,18 @@ private:
     xcb_atom_t send_data(xcb_selection_request_event_t* req);
     xcb_atom_t set_data_property(xcb_selection_request_event_t* req, QByteArray* data);
     SelectionType selection_from_atom(xcb_atom_t selection);
+    xcb_atom_t atom_from_selection(SelectionType selection);
     QString mime_to_QT_img(const QString& mimeType);
+    void delay_selection_request( xcb_selection_request_event_t *reqest, xcb_selection_notify_event_t* event);
+    bool check_req_sanity(xcb_selection_request_event_t* req);
+    void processDelayedRequests();
+    void discardDelayedRequest(uint index);
+    void updateCurrentTime(xcb_timestamp_t t);
+    xcb_timestamp_t currentXTime();
+    void startIncrTransaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, QByteArray data);
+    void process_incr_transaction_property(xcb_property_notify_event_t * pn);
+    void destroy_incr_transaction(int index);
+    void remove_obsolete_incr_transactions(bool checkTS=true);
 
 private slots:
     void checkEvents();
@@ -70,10 +82,43 @@ private:
     xcb_atom_t incrAtom=0;
     uint incrementalSize=0;
     uint incrementalSizeRead=0;
+    xcb_atom_t best_atom[2]; //the best mime type for selection to request on demand sel request from server
+    xcb_atom_t ATOM_CLIPBOARD=0; //atom for CLIPBOARD selection
+
 //input selection
-    SelectionMime selMime[2];
-    QByteArray selData[2];
-    bool owner[2]={0};
-    xcb_timestamp_t timestamp[2];
+
+    struct InputSelection{
+        SelectionMime selMime; //mime of selection (string or image)
+        QByteArray selData; //data
+        bool owner=false; //if we are owners of this selction
+        enum { NOTIFIED, REQUESTED, COMPLETED} state; //state of the selection
+        xcb_timestamp_t timestamp; //timestamp when we own selection
+    }inputSelection[2];
+
+    //requests which processing should be delayed till we get data from server
+    struct DelayedRequest
+    {
+        xcb_selection_request_event_t *request; // request from client
+        xcb_selection_notify_event_t* event; // event which we are going to send to client
+    };
+    QList <DelayedRequest*> delayedSelectionRequests;
+
+    //save running INCR transactions in this struct
+    struct IncrTransaction
+    {
+        xcb_window_t requestor;
+        xcb_atom_t property;
+        xcb_atom_t target;
+        QByteArray data;
+        uint sentBytes;
+        qint64 timestamp;
+    };
+    QList <IncrTransaction*> incrTransactions;
+
+
+    xcb_timestamp_t lastXTime=XCB_TIME_CURRENT_TIME; //current time of X Server
+    int32_t timeDifference=0; //differense between X server time and system time
+
+    uint32_t total_compressed=0;
 };
 #endif

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


More information about the x2go-commits mailing list