summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp200
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h18
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp8
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;