[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, ×tamp[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