1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qxcbclipboard.h"
44 #include "qxcbconnection.h"
45 #include "qxcbscreen.h"
48 #include <private/qguiapplication_p.h>
49 #include <QElapsedTimer>
51 #include <QtCore/QDebug>
53 #define class class_name // Workaround XCB-ICCCM 3.8 breakage
54 #include <xcb/xcb_icccm.h>
59 class QXcbClipboardMime : public QXcbMime
63 QXcbClipboardMime(QClipboard::Mode mode, QXcbClipboard *clipboard)
65 , m_clipboard(clipboard)
68 case QClipboard::Selection:
69 modeAtom = XCB_ATOM_PRIMARY;
72 case QClipboard::Clipboard:
73 modeAtom = m_clipboard->atom(QXcbAtom::CLIPBOARD);
77 qWarning("QXcbClipboardMime: Internal error: Unsupported clipboard mode");
83 QStringList formats_sys() const
88 if (!formatList.count()) {
89 QXcbClipboardMime *that = const_cast<QXcbClipboardMime *>(this);
90 // get the list of targets from the current clipboard owner - we do this
91 // once so that multiple calls to this function don't require multiple
92 // server round trips...
93 that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::TARGETS));
95 if (format_atoms.size() > 0) {
96 xcb_atom_t *targets = (xcb_atom_t *) format_atoms.data();
97 int size = format_atoms.size() / sizeof(xcb_atom_t);
99 for (int i = 0; i < size; ++i) {
103 QString format = mimeAtomToString(m_clipboard->connection(), targets[i]);
104 if (!formatList.contains(format))
105 that->formatList.append(format);
113 bool hasFormat_sys(const QString &format) const
115 QStringList list = formats();
116 return list.contains(format);
119 QVariant retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const
121 if (fmt.isEmpty() || empty())
124 (void)formats(); // trigger update of format list
126 QList<xcb_atom_t> atoms;
127 xcb_atom_t *targets = (xcb_atom_t *) format_atoms.data();
128 int size = format_atoms.size() / sizeof(xcb_atom_t);
129 for (int i = 0; i < size; ++i)
130 atoms.append(targets[i]);
133 xcb_atom_t fmtatom = mimeAtomForFormat(m_clipboard->connection(), fmt, requestedType, atoms, &encoding);
138 return mimeConvertToFormat(m_clipboard->connection(), fmtatom, m_clipboard->getDataInFormat(modeAtom, fmtatom), fmt, requestedType, encoding);
143 return m_clipboard->getSelectionOwner(modeAtom) == XCB_NONE;
148 QXcbClipboard *m_clipboard;
149 QStringList formatList;
150 QByteArray format_atoms;
153 const int QXcbClipboard::clipboard_timeout = 5000;
155 QXcbClipboard::QXcbClipboard(QXcbConnection *c)
156 : QXcbObject(c), QPlatformClipboard()
157 , m_requestor(XCB_NONE)
160 Q_ASSERT(QClipboard::Clipboard == 0);
161 Q_ASSERT(QClipboard::Selection == 1);
162 m_xClipboard[QClipboard::Clipboard] = 0;
163 m_xClipboard[QClipboard::Selection] = 0;
164 m_clientClipboard[QClipboard::Clipboard] = 0;
165 m_clientClipboard[QClipboard::Selection] = 0;
166 m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME;
167 m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME;
169 m_screen = connection()->screens().at(connection()->primaryScreen());
171 int x = 0, y = 0, w = 3, h = 3;
173 m_owner = xcb_generate_id(xcb_connection());
174 Q_XCB_CALL(xcb_create_window(xcb_connection(),
175 XCB_COPY_FROM_PARENT, // depth -- same as root
176 m_owner, // window id
177 m_screen->screen()->root, // parent window id
180 XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
181 m_screen->screen()->root_visual, // visual
185 if (connection()->hasXFixes()) {
186 const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
187 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
188 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
189 Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask));
190 Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask));
194 QXcbClipboard::~QXcbClipboard()
196 // Transfer the clipboard content to the clipboard manager if we own a selection
197 if (m_timestamp[QClipboard::Clipboard] != XCB_CURRENT_TIME ||
198 m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) {
200 // First we check if there is a clipboard manager.
201 xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
202 xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0);
203 if (reply && reply->owner != XCB_NONE) {
204 // we delete the property so the manager saves all TARGETS.
205 xcb_delete_property(xcb_connection(), m_owner, atom(QXcbAtom::_QT_SELECTION));
206 xcb_convert_selection(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD_MANAGER), atom(QXcbAtom::SAVE_TARGETS),
207 atom(QXcbAtom::_QT_SELECTION), connection()->time());
208 connection()->sync();
210 // waiting until the clipboard manager fetches the content.
211 if (!waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, clipboard_timeout, true)) {
212 qWarning("QXcbClipboard: Unable to receive an event from the "
213 "clipboard manager in a reasonable time");
221 xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const
223 xcb_connection_t *c = xcb_connection();
224 xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom);
225 xcb_get_selection_owner_reply_t *reply;
226 reply = xcb_get_selection_owner_reply(c, cookie, 0);
227 xcb_window_t win = reply->owner;
232 xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const
234 if (mode == QClipboard::Clipboard)
235 return atom(QXcbAtom::CLIPBOARD);
236 if (mode == QClipboard::Selection)
237 return XCB_ATOM_PRIMARY;
241 QClipboard::Mode QXcbClipboard::modeForAtom(xcb_atom_t a) const
243 if (a == XCB_ATOM_PRIMARY)
244 return QClipboard::Selection;
245 if (a == atom(QXcbAtom::CLIPBOARD))
246 return QClipboard::Clipboard;
247 // not supported enum value, used to detect errors
248 return QClipboard::FindBuffer;
252 QMimeData * QXcbClipboard::mimeData(QClipboard::Mode mode)
254 if (mode > QClipboard::Selection)
257 xcb_window_t clipboardOwner = getSelectionOwner(atomForMode(mode));
258 if (clipboardOwner == owner()) {
259 return m_clientClipboard[mode];
261 if (!m_xClipboard[mode])
262 m_xClipboard[mode] = new QXcbClipboardMime(mode, this);
264 return m_xClipboard[mode];
268 void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
270 if (mode > QClipboard::Selection)
273 xcb_atom_t modeAtom = atomForMode(mode);
275 if (m_clientClipboard[mode] == data)
278 xcb_window_t newOwner = XCB_NONE;
280 if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection])
281 delete m_clientClipboard[mode];
282 m_clientClipboard[mode] = 0;
283 m_timestamp[mode] = XCB_CURRENT_TIME;
288 m_clientClipboard[mode] = data;
289 m_timestamp[mode] = connection()->time();
292 xcb_set_selection_owner(xcb_connection(), newOwner, modeAtom, connection()->time());
294 if (getSelectionOwner(modeAtom) != newOwner) {
295 qWarning("QXcbClipboard::setData: Cannot set X11 selection owner");
301 bool QXcbClipboard::supportsMode(QClipboard::Mode mode) const
303 if (mode <= QClipboard::Selection)
308 bool QXcbClipboard::ownsMode(QClipboard::Mode mode) const
310 if (m_owner == XCB_NONE || mode > QClipboard::Selection)
313 Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME || getSelectionOwner(atomForMode(mode)) == m_owner);
315 return m_timestamp[mode] != XCB_CURRENT_TIME;
318 xcb_window_t QXcbClipboard::requestor() const
321 const int x = 0, y = 0, w = 3, h = 3;
322 QXcbClipboard *that = const_cast<QXcbClipboard *>(this);
324 xcb_window_t window = xcb_generate_id(xcb_connection());
325 Q_XCB_CALL(xcb_create_window(xcb_connection(),
326 XCB_COPY_FROM_PARENT, // depth -- same as root
328 m_screen->screen()->root, // parent window id
331 XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
332 m_screen->screen()->root_visual, // visual
336 uint32_t mask = XCB_EVENT_MASK_PROPERTY_CHANGE;
337 xcb_change_window_attributes(xcb_connection(), window, XCB_CW_EVENT_MASK, &mask);
339 that->setRequestor(window);
344 void QXcbClipboard::setRequestor(xcb_window_t window)
346 if (m_requestor != XCB_NONE) {
347 xcb_destroy_window(xcb_connection(), m_requestor);
349 m_requestor = window;
352 xcb_window_t QXcbClipboard::owner() const
357 xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property)
359 QVector<xcb_atom_t> types;
360 QStringList formats = QInternalMimeData::formatsHelper(d);
361 for (int i = 0; i < formats.size(); ++i) {
362 QList<xcb_atom_t> atoms = QXcbMime::mimeAtomsForFormat(connection(), formats.at(i));
363 for (int j = 0; j < atoms.size(); ++j) {
364 if (!types.contains(atoms.at(j)))
365 types.append(atoms.at(j));
368 types.append(atom(QXcbAtom::TARGETS));
369 types.append(atom(QXcbAtom::MULTIPLE));
370 types.append(atom(QXcbAtom::TIMESTAMP));
371 types.append(atom(QXcbAtom::SAVE_TARGETS));
373 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, XCB_ATOM_ATOM,
374 32, types.size(), (const void *)types.constData());
378 xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_window_t window, xcb_atom_t property)
380 xcb_atom_t atomFormat = target;
384 QString fmt = QXcbMime::mimeAtomToString(connection(), target);
385 if (fmt.isEmpty()) { // Not a MIME type we have
386 // qDebug() << "QClipboard: send_selection(): converting to type" << connection()->atomName(target) << "is not supported";
389 // qDebug() << "QClipboard: send_selection(): converting to type" << fmt;
391 if (QXcbMime::mimeDataForAtom(connection(), target, d, &data, &atomFormat, &dataFormat)) {
393 // don't allow INCR transfers when using MULTIPLE or to
394 // Motif clients (since Motif doesn't support INCR)
395 static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::CLIP_TEMPORARY);
396 bool allow_incr = property != motif_clip_temporary;
398 // X_ChangeProperty protocol request is 24 bytes
399 const int increment = (xcb_get_maximum_request_length(xcb_connection()) * 4) - 24;
400 if (data.size() > increment && allow_incr) {
401 long bytes = data.size();
402 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property,
403 atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes);
405 // (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment);
406 qWarning("QXcbClipboard: INCR is unimplemented");
410 // make sure we can perform the XChangeProperty in a single request
411 if (data.size() > increment)
412 return XCB_NONE; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
413 int dataSize = data.size() / (dataFormat / 8);
414 // use a single request to transfer data
415 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, atomFormat,
416 dataFormat, dataSize, (const void *)data.constData());
421 void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *event)
423 QClipboard::Mode mode = modeForAtom(event->selection);
424 if (mode > QClipboard::Selection)
427 // ignore the event if it was generated before we gained selection ownership
428 if (m_timestamp[mode] != XCB_CURRENT_TIME && event->time <= m_timestamp[mode])
431 // DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)",
432 // XGetSelectionOwner(dpy, XA_PRIMARY),
433 // xevent->xselectionclear.time, d->timestamp);
435 // if (!waiting_for_data) {
436 setMimeData(0, mode);
439 // pending_selection_changed = true;
440 // if (! pending_timer_id)
441 // pending_timer_id = QApplication::clipboard()->startTimer(0);
445 void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
447 if (requestor() && req->requestor == requestor()) {
448 qWarning("QXcbClipboard: Selection request should be caught before");
452 xcb_selection_notify_event_t event;
453 event.response_type = XCB_SELECTION_NOTIFY;
454 event.requestor = req->requestor;
455 event.selection = req->selection;
456 event.target = req->target;
457 event.property = XCB_NONE;
458 event.time = req->time;
461 QClipboard::Mode mode = modeForAtom(req->selection);
462 if (mode > QClipboard::Selection) {
463 qWarning() << "QXcbClipboard: Unknown selection" << connection()->atomName(req->selection);
464 xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
468 d = m_clientClipboard[mode];
471 qWarning("QXcbClipboard: Cannot transfer data, no data available");
472 xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
476 if (m_timestamp[mode] == XCB_CURRENT_TIME // we don't own the selection anymore
477 || (req->time != XCB_CURRENT_TIME && req->time < m_timestamp[mode])) {
478 qWarning("QXcbClipboard: SelectionRequest too old");
479 xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
483 xcb_atom_t xa_targets = atom(QXcbAtom::TARGETS);
484 xcb_atom_t xa_multiple = atom(QXcbAtom::MULTIPLE);
485 xcb_atom_t xa_timestamp = atom(QXcbAtom::TIMESTAMP);
487 struct AtomPair { xcb_atom_t target; xcb_atom_t property; } *multi = 0;
488 xcb_atom_t multi_type = XCB_NONE;
489 int multi_format = 0;
492 bool multi_writeback = false;
494 if (req->target == xa_multiple) {
495 QByteArray multi_data;
496 if (req->property == XCB_NONE
497 || !clipboardReadProperty(req->requestor, req->property, false, &multi_data,
498 0, &multi_type, &multi_format)
499 || multi_format != 32) {
500 // MULTIPLE property not formatted correctly
501 xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
504 nmulti = multi_data.size()/sizeof(*multi);
505 multi = new AtomPair[nmulti];
506 memcpy(multi,multi_data.data(),multi_data.size());
510 for (; imulti < nmulti; ++imulti) {
515 target = multi[imulti].target;
516 property = multi[imulti].property;
518 target = req->target;
519 property = req->property;
520 if (property == XCB_NONE) // obsolete client
524 xcb_atom_t ret = XCB_NONE;
525 if (target == XCB_NONE || property == XCB_NONE) {
527 } else if (target == xa_timestamp) {
528 if (m_timestamp[mode] != XCB_CURRENT_TIME) {
529 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, req->requestor,
530 property, XCB_ATOM_INTEGER, 32, 1, &m_timestamp[mode]);
533 qWarning("QXcbClipboard: Invalid data timestamp");
535 } else if (target == xa_targets) {
536 ret = sendTargetsSelection(d, req->requestor, property);
538 ret = sendSelection(d, target, req->requestor, property);
542 if (ret == XCB_NONE) {
543 multi[imulti].property = XCB_NONE;
544 multi_writeback = true;
547 event.property = ret;
553 if (multi_writeback) {
554 // according to ICCCM 2.6.2 says to put None back
555 // into the original property on the requestor window
556 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, req->requestor, req->property,
557 multi_type, 32, nmulti*2, (const void *)multi);
561 event.property = req->property;
564 // send selection notify to requestor
565 xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
568 void QXcbClipboard::handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event)
570 QClipboard::Mode mode = modeForAtom(event->selection);
571 if (event->owner != owner() && m_clientClipboard[mode] && m_timestamp[mode] < event->selection_timestamp) {
572 setMimeData(0, mode);
578 static inline int maxSelectionIncr(xcb_connection_t *c)
580 int l = xcb_get_maximum_request_length(c);
581 return (l > 65536 ? 65536*4 : l*4) - 100;
584 bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const
586 int maxsize = maxSelectionIncr(xcb_connection());
587 ulong bytes_left; // bytes_after
588 xcb_atom_t dummy_type;
591 if (!type) // allow null args
594 format = &dummy_format;
596 // Don't read anything, just get the size of the property data
597 xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
598 xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
599 if (!reply || reply->type == XCB_NONE) {
605 *format = reply->format;
606 bytes_left = reply->bytes_after;
609 int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
614 format_inc = sizeof(char) / 1;
618 format_inc = sizeof(short) / 2;
619 proplen *= sizeof(short) / 2;
623 format_inc = sizeof(long) / 4;
624 proplen *= sizeof(long) / 4;
628 int newSize = proplen;
629 buffer->resize(newSize);
631 bool ok = (buffer->size() == newSize);
634 // could allocate buffer
639 xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4));
640 reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
641 if (!reply || reply->type == XCB_NONE) {
646 *format = reply->format;
647 bytes_left = reply->bytes_after;
648 char *data = (char *)xcb_get_property_value(reply);
649 int length = xcb_get_property_value_length(reply);
651 offset += length / (32 / *format);
652 length *= format_inc * (*format) / 8;
654 // Here we check if we get a buffer overflow and tries to
655 // recover -- this shouldn't normally happen, but it doesn't
656 // hurt to be defensive
657 if ((int)(buffer_offset + length) > buffer->size()) {
658 length = buffer->size() - buffer_offset;
664 memcpy(buffer->data() + buffer_offset, data, length);
665 buffer_offset += length;
672 // correct size, not 0-term.
674 *size = buffer_offset;
677 xcb_delete_property(xcb_connection(), win, property);
679 connection()->flush();
689 Notify(xcb_window_t win, int t)
690 : window(win), type(t) {}
693 bool checkEvent(xcb_generic_event_t *event) const {
696 int t = event->response_type & 0x7f;
699 if (t == XCB_PROPERTY_NOTIFY) {
700 xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
701 if (pn->window == window)
703 } else if (t == XCB_SELECTION_NOTIFY) {
704 xcb_selection_notify_event_t *sn = (xcb_selection_notify_event_t *)event;
705 if (sn->requestor == window)
711 class ClipboardEvent {
713 ClipboardEvent(QXcbConnection *c)
714 { clipboard = c->internAtom("CLIPBOARD"); }
715 xcb_atom_t clipboard;
716 bool checkEvent(xcb_generic_event_t *e) const {
719 int type = e->response_type & 0x7f;
720 if (type == XCB_SELECTION_REQUEST) {
721 xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)e;
722 return sr->selection == XCB_ATOM_PRIMARY || sr->selection == clipboard;
723 } else if (type == XCB_SELECTION_CLEAR) {
724 xcb_selection_clear_event_t *sc = (xcb_selection_clear_event_t *)e;
725 return sc->selection == XCB_ATOM_PRIMARY || sc->selection == clipboard;
732 xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int type, int timeout, bool checkManager)
737 Notify notify(win, type);
738 xcb_generic_event_t *e = connection()->checkEvent(notify);
743 xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
744 xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0);
745 if (!reply || reply->owner == XCB_NONE) {
752 // process other clipboard events, since someone is probably requesting data from us
753 ClipboardEvent clipboard(connection());
754 e = connection()->checkEvent(clipboard);
756 connection()->handleXcbEvent(e);
760 connection()->flush();
762 // sleep 50 ms, so we don't use up CPU cycles all the time.
763 struct timeval usleep_tv;
764 usleep_tv.tv_sec = 0;
765 usleep_tv.tv_usec = 50000;
766 select(0, 0, 0, 0, &usleep_tv);
767 } while (timer.elapsed() < timeout);
772 QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm)
776 bool alloc_error = false;
781 // Reserve buffer + zero-terminator (for text data)
782 // We want to complete the INCR transfer even if we cannot
783 // allocate more memory
784 buf.resize(nbytes+1);
785 alloc_error = buf.size() != nbytes+1;
789 connection()->flush();
790 xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout);
794 xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge;
795 if (event->atom != property || event->state != XCB_PROPERTY_NEW_VALUE)
797 if (clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) {
798 if (length == 0) { // no more data, we're done
800 buf.resize(offset+1);
806 } else if (!alloc_error) {
807 if (offset+length > (int)buf.size()) {
808 buf.resize(offset+length+65535);
809 if (buf.size() != offset+length+65535) {
811 length = buf.size() - offset;
814 memcpy(buf.data()+offset, tmp_buf.constData(), length);
825 // timed out ... create a new requestor window, otherwise the requestor
826 // could consider next request to be still part of this timed out request
832 QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom)
834 return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::_QT_SELECTION));
837 QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property)
840 xcb_window_t win = requestor();
842 xcb_delete_property(xcb_connection(), win, property);
843 xcb_convert_selection(xcb_connection(), win, selection, target, property, connection()->time());
845 connection()->sync();
847 xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_SELECTION_NOTIFY, clipboard_timeout);
848 bool no_selection = !ge || ((xcb_selection_notify_event_t *)ge)->property == XCB_NONE;
855 if (clipboardReadProperty(win, property, true, &buf, 0, &type, 0)) {
856 if (type == atom(QXcbAtom::INCR)) {
857 int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
858 buf = clipboardReadIncrementalProperty(win, property, nbytes, false);
867 #include "qxcbclipboard.moc"