diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.cpp | 200 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.h | 18 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 8 |
3 files changed, 149 insertions, 77 deletions
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index e25192fcd7..ea7cefb972 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -150,16 +150,47 @@ const int QXcbClipboard::clipboard_timeout = 5000; QXcbClipboard::QXcbClipboard(QXcbConnection *c) : QXcbObject(c), QPlatformClipboard() - , m_xClipboard(0) - , m_clientClipboard(0) - , m_xSelection(0) - , m_clientSelection(0) , m_requestor(XCB_NONE) , m_owner(XCB_NONE) { + Q_ASSERT(QClipboard::Clipboard == 0); + Q_ASSERT(QClipboard::Selection == 1); + m_xClipboard[QClipboard::Clipboard] = 0; + m_xClipboard[QClipboard::Selection] = 0; + m_clientClipboard[QClipboard::Clipboard] = 0; + m_clientClipboard[QClipboard::Selection] = 0; + m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME; + m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME; + m_screen = connection()->screens().at(connection()->primaryScreen()); } +QXcbClipboard::~QXcbClipboard() +{ + // 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) { + + // First we check if there is a clipboard manager. + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER)); + xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0); + if (reply && reply->owner != XCB_NONE) { + // we delete the property so the manager saves all TARGETS. + xcb_delete_property(xcb_connection(), m_owner, atom(QXcbAtom::_QT_SELECTION)); + xcb_convert_selection(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD_MANAGER), atom(QXcbAtom::SAVE_TARGETS), + atom(QXcbAtom::_QT_SELECTION), connection()->time()); + connection()->sync(); + + // waiting until the clipboard manager fetches the content. + if (!waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, 5000)) { + qWarning("QClipboard: Unable to receive an event from the " + "clipboard manager in a reasonable time"); + } + } + } +} + + xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const { xcb_connection_t *c = xcb_connection(); @@ -171,60 +202,63 @@ xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const return win; } +xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const +{ + if (mode == QClipboard::Clipboard) + return atom(QXcbAtom::CLIPBOARD); + if (mode == QClipboard::Selection) + return XCB_ATOM_PRIMARY; + return XCB_NONE; +} + +QClipboard::Mode QXcbClipboard::modeForAtom(xcb_atom_t a) const +{ + if (a == XCB_ATOM_PRIMARY) + return QClipboard::Selection; + if (a == atom(QXcbAtom::CLIPBOARD)) + return QClipboard::Clipboard; + // not supported enum value, used to detect errors + return QClipboard::FindBuffer; +} + + QMimeData * QXcbClipboard::mimeData(QClipboard::Mode mode) { - if (mode == QClipboard::Clipboard) { - if (!m_xClipboard) { - m_xClipboard = new QXcbClipboardMime(mode, this); - } - xcb_window_t clipboardOwner = getSelectionOwner(atom(QXcbAtom::CLIPBOARD)); - if (clipboardOwner == owner()) { - return m_clientClipboard; - } else { - return m_xClipboard; - } - } else if (mode == QClipboard::Selection) { - if (!m_xSelection) { - m_xSelection = new QXcbClipboardMime(mode, this); - } - xcb_window_t clipboardOwner = getSelectionOwner(XCB_ATOM_PRIMARY); - if (clipboardOwner == owner()) { - return m_clientSelection; - } else { - return m_xSelection; - } + if (mode > QClipboard::Selection) + return 0; + + xcb_window_t clipboardOwner = getSelectionOwner(atomForMode(mode)); + if (clipboardOwner == owner()) { + return m_clientClipboard[mode]; + } else { + if (!m_xClipboard[mode]) + m_xClipboard[mode] = new QXcbClipboardMime(mode, this); + + return m_xClipboard[mode]; } - return 0; } void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) { - xcb_atom_t modeAtom; - QMimeData **d; - switch (mode) { - case QClipboard::Selection: - modeAtom = XCB_ATOM_PRIMARY; - d = &m_clientSelection; - break; + if (mode > QClipboard::Selection) + return; - case QClipboard::Clipboard: - modeAtom = atom(QXcbAtom::CLIPBOARD); - d = &m_clientClipboard; - break; + xcb_atom_t modeAtom = atomForMode(mode); - default: - qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode); + if (m_clientClipboard[mode] == data) return; - } - xcb_window_t newOwner; + xcb_window_t newOwner = XCB_NONE; - if (! data) { // no data, clear clipboard contents - newOwner = XCB_NONE; - } else { + delete m_clientClipboard[mode]; + m_clientClipboard[mode] = 0; + m_timestamp[mode] = XCB_CURRENT_TIME; + + if (data) { newOwner = owner(); - *d = data; + m_clientClipboard[mode] = data; + m_timestamp[mode] = connection()->time(); } xcb_set_selection_owner(xcb_connection(), newOwner, modeAtom, connection()->time()); @@ -237,11 +271,21 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) bool QXcbClipboard::supportsMode(QClipboard::Mode mode) const { - if (mode == QClipboard::Clipboard || mode == QClipboard::Selection) + if (mode <= QClipboard::Selection) return true; return false; } +bool QXcbClipboard::ownsMode(QClipboard::Mode mode) const +{ + if (m_owner == XCB_NONE || mode > QClipboard::Selection) + return false; + + Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME || getSelectionOwner(atomForMode(mode)) == m_owner); + + return m_timestamp[mode] != XCB_CURRENT_TIME; +} + xcb_window_t QXcbClipboard::requestor() const { if (!m_requestor) { @@ -280,7 +324,6 @@ xcb_window_t QXcbClipboard::owner() const { if (!m_owner) { int x = 0, y = 0, w = 3, h = 3; - QXcbClipboard *that = const_cast<QXcbClipboard *>(this); xcb_window_t window = xcb_generate_id(xcb_connection()); Q_XCB_CALL(xcb_create_window(xcb_connection(), @@ -294,19 +337,12 @@ xcb_window_t QXcbClipboard::owner() const 0, // value mask 0)); // value list - that->setOwner(window); + QXcbClipboard *that = const_cast<QXcbClipboard *>(this); + that->m_owner = window; } return m_owner; } -void QXcbClipboard::setOwner(xcb_window_t window) -{ - if (m_owner != XCB_NONE){ - xcb_destroy_window(xcb_connection(), m_owner); - } - m_owner = window; -} - xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property) { QVector<xcb_atom_t> types; @@ -371,6 +407,30 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win return property; } +void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *event) +{ + QClipboard::Mode mode = modeForAtom(event->selection); + if (mode > QClipboard::Selection) + return; + + // ignore the event if it was generated before we gained selection ownership + if (m_timestamp[mode] != XCB_CURRENT_TIME && event->time <= m_timestamp[mode]) + return; + +// DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)", +// XGetSelectionOwner(dpy, XA_PRIMARY), +// xevent->xselectionclear.time, d->timestamp); + +// if (!waiting_for_data) { + setMimeData(0, mode); + emitChanged(mode); +// } else { +// pending_selection_changed = true; +// if (! pending_timer_id) +// pending_timer_id = QApplication::clipboard()->startTimer(0); +// } +} + void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) { if (requestor() && req->requestor == requestor()) { @@ -387,22 +447,28 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) event.time = req->time; QMimeData *d; - if (req->selection == XCB_ATOM_PRIMARY) { - d = m_clientSelection; - } else if (req->selection == atom(QXcbAtom::CLIPBOARD)) { - d = m_clientClipboard; - } else { + QClipboard::Mode mode = modeForAtom(req->selection); + if (mode > QClipboard::Selection) { qWarning() << "QClipboard: Unknown selection" << connection()->atomName(req->selection); xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); return; } + d = m_clientClipboard[mode]; + if (!d) { qWarning("QClipboard: Cannot transfer data, no data available"); xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); return; } + if (m_timestamp[mode] == XCB_CURRENT_TIME // we don't own the selection anymore + || (req->time != XCB_CURRENT_TIME && req->time < m_timestamp[mode])) { + qDebug("QClipboard: SelectionRequest too old"); + xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + return; + } + xcb_atom_t xa_targets = atom(QXcbAtom::TARGETS); xcb_atom_t xa_multiple = atom(QXcbAtom::MULTIPLE); xcb_atom_t xa_timestamp = atom(QXcbAtom::TIMESTAMP); @@ -448,13 +514,13 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) if (target == XCB_NONE || property == XCB_NONE) { ; } else if (target == xa_timestamp) { -// if (d->timestamp != CurrentTime) { -// XChangeProperty(DISPLAY_FROM_XCB(connection()), req->requestor, property, QXcbAtom::XA_INTEGER, 32, -// PropModeReplace, CurrentTime, 1); -// ret = property; -// } else { -// qWarning("QClipboard: Invalid data timestamp"); -// } + if (m_timestamp[mode] != XCB_CURRENT_TIME) { + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, req->requestor, + property, XCB_ATOM_INTEGER, 32, 1, &m_timestamp[mode]); + ret = property; + } else { + qWarning("QClipboard: Invalid data timestamp"); + } } else if (target == xa_targets) { ret = sendTargetsSelection(d, req->requestor, property); } else { diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h index ff2d44144e..db31051ee7 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.h +++ b/src/plugins/platforms/xcb/qxcbclipboard.h @@ -48,16 +48,19 @@ class QXcbConnection; class QXcbScreen; +class QXcbClipboardMime; class QXcbClipboard : public QXcbObject, public QPlatformClipboard { public: QXcbClipboard(QXcbConnection *connection); + ~QXcbClipboard(); QMimeData *mimeData(QClipboard::Mode mode); void setMimeData(QMimeData *data, QClipboard::Mode mode); bool supportsMode(QClipboard::Mode mode) const; + bool ownsMode(QClipboard::Mode mode) const; QXcbScreen *screen() const { return m_screen; } @@ -67,6 +70,7 @@ public: xcb_window_t owner() const; void handleSelectionRequest(xcb_selection_request_event_t *event); + void handleSelectionClearRequest(xcb_selection_clear_event_t *event); bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const; QByteArray clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm); @@ -77,20 +81,20 @@ public: QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property); private: - void setOwner(xcb_window_t window); - xcb_generic_event_t *waitForClipboardEvent(xcb_window_t win, int type, int timeout); xcb_atom_t sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property); xcb_atom_t sendSelection(QMimeData *d, xcb_atom_t target, xcb_window_t window, xcb_atom_t property); - QXcbScreen *m_screen; + xcb_atom_t atomForMode(QClipboard::Mode mode) const; + QClipboard::Mode modeForAtom(xcb_atom_t atom) const; - QMimeData *m_xClipboard; - QMimeData *m_clientClipboard; + QXcbScreen *m_screen; - QMimeData *m_xSelection; - QMimeData *m_clientSelection; + // Selection and Clipboard + QXcbClipboardMime *m_xClipboard[2]; + QMimeData *m_clientClipboard[2]; + xcb_timestamp_t m_timestamp[2]; xcb_window_t m_requestor; xcb_window_t m_owner; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 0af71fa830..eddc3305d1 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -148,6 +148,8 @@ QXcbConnection::QXcbConnection(const char *displayName) QXcbConnection::~QXcbConnection() { + delete m_clipboard; + qDeleteAll(m_screens); #ifdef XCB_USE_XLIB @@ -157,7 +159,6 @@ QXcbConnection::~QXcbConnection() #endif delete m_keyboard; - delete m_clipboard; } void QXcbConnection::addWindow(xcb_window_t id, QXcbWindow *window) @@ -488,10 +489,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) } case XCB_SELECTION_CLEAR: setTime(((xcb_selection_clear_event_t *)event)->time); - qDebug() << "XCB_SELECTION_CLEAR"; - handled = false; + m_clipboard->handleSelectionClearRequest((xcb_selection_clear_event_t *)event); + handled = true; break; case XCB_SELECTION_NOTIFY: + setTime(((xcb_selection_notify_event_t *)event)->time); qDebug() << "XCB_SELECTION_NOTIFY"; handled = false; break; |