diff options
author | Gatis Paeglis <gatis.paeglis@digia.com> | 2013-09-09 14:20:50 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 12:56:56 +0200 |
commit | bc6f5b3ff61f4b1dea14084349702f2895feda66 (patch) | |
tree | 38b7549734c5fe3f7030a0e81bc0e3c9a3143ad9 | |
parent | fdd843d5a76f2bba5c4ebdd5813c97f5105c5765 (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>
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.cpp | 130 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 6 |
3 files changed, 138 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; } diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h index 61cdce3d1d..c94762d80f 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.h +++ b/src/plugins/platforms/xcb/qxcbclipboard.h @@ -83,6 +83,10 @@ public: QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom); + void setProcessIncr(bool process) { m_incr_active = process; } + bool processIncr() { return m_incr_active; } + void incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepted); + xcb_window_t getSelectionOwner(xcb_atom_t atom) const; QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0); @@ -107,6 +111,8 @@ private: static const int clipboard_timeout; + bool m_incr_active; + bool m_clipboard_closing; }; #endif // QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 2ce34ea8f2..bb9922c5a1 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1154,6 +1154,12 @@ void QXcbConnection::processXcbEvents() continue; } + bool accepted = false; + if (clipboard()->processIncr()) + clipboard()->incrTransactionPeeker(event, accepted); + if (accepted) + continue; + QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); while (it != m_peekFuncs.end()) { // These callbacks return true if the event is what they were |