summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbclipboard.cpp
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@digia.com>2013-09-09 14:20:50 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-19 12:56:56 +0200
commitbc6f5b3ff61f4b1dea14084349702f2895feda66 (patch)
tree38b7549734c5fe3f7030a0e81bc0e3c9a3143ad9 /src/plugins/platforms/xcb/qxcbclipboard.cpp
parentfdd843d5a76f2bba5c4ebdd5813c97f5105c5765 (diff)
Implement INCR property mechanism for large data transfers [XCB]
Implement INCR property mechanism according to the icccm specification. Change-Id: Ic8f85b71cab825d70ee1b61f29acd09fa4c3e642 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbclipboard.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp130
1 files changed, 126 insertions, 4 deletions
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index ab48d18af4..dab4f443ff 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -157,12 +157,121 @@ private:
QByteArray format_atoms;
};
+class INCRTransaction;
+typedef QMap<xcb_window_t,INCRTransaction*> TransactionMap;
+static TransactionMap *transactions = 0;
+
+//#define INCR_DEBUG
+
+class INCRTransaction : public QObject
+{
+ Q_OBJECT
+public:
+ INCRTransaction(QXcbConnection *c, xcb_window_t w, xcb_atom_t p,
+ QByteArray d, uint i, xcb_atom_t t, int f, int to) :
+ conn(c), win(w), property(p), data(d), increment(i),
+ target(t), format(f), timeout(to), offset(0)
+ {
+ const quint32 values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+ xcb_change_window_attributes(conn->xcb_connection(), win,
+ XCB_CW_EVENT_MASK, values);
+ if (!transactions) {
+#ifdef INCR_DEBUG
+ qDebug("INCRTransaction: creating the TransactionMap");
+#endif
+ transactions = new TransactionMap;
+ conn->clipboard()->setProcessIncr(true);
+ }
+ transactions->insert(win, this);
+ abort_timer = startTimer(timeout);
+ }
+
+ ~INCRTransaction()
+ {
+ if (abort_timer)
+ killTimer(abort_timer);
+ abort_timer = 0;
+ transactions->remove(win);
+ if (transactions->isEmpty()) {
+#ifdef INCR_DEBUG
+ qDebug("INCRTransaction: no more INCR transactions left in the TransactionMap");
+#endif
+ delete transactions;
+ transactions = 0;
+ conn->clipboard()->setProcessIncr(false);
+ }
+ }
+
+ void updateIncrProperty(xcb_property_notify_event_t *event, bool &accepted)
+ {
+ xcb_connection_t *c = conn->xcb_connection();
+ if (event->atom == property && event->state == XCB_PROPERTY_DELETE) {
+ accepted = true;
+ // restart the timer
+ if (abort_timer)
+ killTimer(abort_timer);
+ abort_timer = startTimer(timeout);
+
+ unsigned int bytes_left = data.size() - offset;
+ if (bytes_left > 0) {
+ unsigned int bytes_to_send = qMin(increment, bytes_left);
+#ifdef INCR_DEBUG
+ qDebug("INCRTransaction: sending %d bytes, %d remaining (INCR transaction %p)",
+ bytes_to_send, bytes_left - bytes_to_send, this);
+#endif
+ int dataSize = bytes_to_send / (format / 8);
+ xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, property,
+ target, format, dataSize, data.constData() + offset);
+ offset += bytes_to_send;
+ } else {
+#ifdef INCR_DEBUG
+ qDebug("INCRTransaction: INCR transaction %p completed", this);
+#endif
+ xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, property,
+ target, format, 0, (const void *)0);
+ const quint32 values[] = { XCB_EVENT_MASK_NO_EVENT };
+ xcb_change_window_attributes(conn->xcb_connection(), win,
+ XCB_CW_EVENT_MASK, values);
+ // self destroy
+ delete this;
+ }
+ }
+ }
+
+protected:
+ void timerEvent(QTimerEvent *ev)
+ {
+ if (ev->timerId() == abort_timer) {
+ // this can happen when the X client we are sending data
+ // to decides to exit (normally or abnormally)
+#ifdef INCR_DEBUG
+ qDebug("INCRTransaction: Timed out while sending data to %p", this);
+#endif
+ delete this;
+ }
+ }
+
+private:
+ QXcbConnection *conn;
+ xcb_window_t win;
+ xcb_atom_t property;
+ QByteArray data;
+ uint increment;
+ xcb_atom_t target;
+ int format;
+ int timeout;
+ uint offset;
+ int abort_timer;
+};
+
const int QXcbClipboard::clipboard_timeout = 5000;
QXcbClipboard::QXcbClipboard(QXcbConnection *c)
: QXcbObject(c), QPlatformClipboard()
, m_requestor(XCB_NONE)
, m_owner(XCB_NONE)
+ , m_incr_active(false)
+ , m_clipboard_closing(false)
{
Q_ASSERT(QClipboard::Clipboard == 0);
Q_ASSERT(QClipboard::Selection == 1);
@@ -200,6 +309,7 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
QXcbClipboard::~QXcbClipboard()
{
+ m_clipboard_closing = true;
// Transfer the clipboard content to the clipboard manager if we own a selection
if (m_timestamp[QClipboard::Clipboard] != XCB_CURRENT_TIME ||
m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) {
@@ -224,6 +334,17 @@ QXcbClipboard::~QXcbClipboard()
}
}
+void QXcbClipboard::incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepted)
+{
+ uint response_type = ge->response_type & ~0x80;
+ if (response_type == XCB_PROPERTY_NOTIFY) {
+ xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge;
+ TransactionMap::Iterator it = transactions->find(event->window);
+ if (it != transactions->end()) {
+ (*it)->updateIncrProperty(event, accepted);
+ }
+ }
+}
xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const
{
@@ -415,16 +536,17 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win
// Motif clients (since Motif doesn't support INCR)
static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::CLIP_TEMPORARY);
bool allow_incr = property != motif_clip_temporary;
-
+ // This 'bool' can be removed once there is a proper fix for QTBUG-32853
+ if (m_clipboard_closing)
+ allow_incr = false;
// X_ChangeProperty protocol request is 24 bytes
const int increment = (xcb_get_maximum_request_length(xcb_connection()) * 4) - 24;
if (data.size() > increment && allow_incr) {
long bytes = data.size();
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property,
atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes);
-
-// (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment);
- qWarning("QXcbClipboard: INCR is unimplemented");
+ new INCRTransaction(connection(), window, property, data, increment,
+ atomFormat, dataFormat, clipboard_timeout);
return property;
}