diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbclipboard.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.cpp | 186 |
1 files changed, 77 insertions, 109 deletions
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 736b56ef60..77157595b8 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qxcbclipboard.h" @@ -67,11 +31,11 @@ public: break; case QClipboard::Clipboard: - modeAtom = m_clipboard->atom(QXcbAtom::CLIPBOARD); + modeAtom = m_clipboard->atom(QXcbAtom::AtomCLIPBOARD); break; default: - qWarning("QXcbClipboardMime: Internal error: Unsupported clipboard mode"); + qCWarning(lcQpaClipboard, "QXcbClipboardMime: Internal error: Unsupported clipboard mode"); break; } } @@ -83,7 +47,7 @@ public: bool isEmpty() const { - return m_clipboard->getSelectionOwner(modeAtom) == XCB_NONE; + return m_clipboard->connection()->selectionOwner(modeAtom) == XCB_NONE; } protected: @@ -92,12 +56,12 @@ protected: if (isEmpty()) return QStringList(); - if (!formatList.count()) { + if (!formatList.size()) { QXcbClipboardMime *that = const_cast<QXcbClipboardMime *>(this); // get the list of targets from the current clipboard owner - we do this // once so that multiple calls to this function don't require multiple // server round trips... - that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::TARGETS)); + that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::AtomTARGETS)).value_or(QByteArray()); if (format_atoms.size() > 0) { const xcb_atom_t *targets = (const xcb_atom_t *) format_atoms.data(); @@ -127,7 +91,7 @@ protected: { auto requestedType = type; if (fmt.isEmpty() || isEmpty()) - return QByteArray(); + return QVariant(); (void)formats(); // trigger update of format list @@ -144,7 +108,11 @@ protected: if (fmtatom == 0) return QVariant(); - return mimeConvertToFormat(m_clipboard->connection(), fmtatom, m_clipboard->getDataInFormat(modeAtom, fmtatom), fmt, requestedType, hasUtf8); + const std::optional<QByteArray> result = m_clipboard->getDataInFormat(modeAtom, fmtatom); + if (!result.has_value()) + return QVariant(); + + return mimeConvertToFormat(m_clipboard->connection(), fmtatom, result.value(), fmt, requestedType, hasUtf8); } private: @@ -162,7 +130,7 @@ QXcbClipboardTransaction::QXcbClipboardTransaction(QXcbClipboard *clipboard, xcb xcb_change_window_attributes(m_clipboard->xcb_connection(), m_window, XCB_CW_EVENT_MASK, values); - m_abortTimerId = startTimer(m_clipboard->clipboardTimeout()); + m_abortTimerId = startTimer(std::chrono::milliseconds{m_clipboard->clipboardTimeout()}); } QXcbClipboardTransaction::~QXcbClipboardTransaction() @@ -181,7 +149,7 @@ bool QXcbClipboardTransaction::updateIncrementalProperty(const xcb_property_noti // restart the timer if (m_abortTimerId) killTimer(m_abortTimerId); - m_abortTimerId = startTimer(m_clipboard->clipboardTimeout()); + m_abortTimerId = startTimer(std::chrono::milliseconds{m_clipboard->clipboardTimeout()}); uint bytes_left = uint(m_data.size()) - m_offset; if (bytes_left > 0) { @@ -231,14 +199,15 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c) m_clientClipboard[QClipboard::Selection] = nullptr; m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME; m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME; - m_owner = connection()->getQtSelectionOwner(); if (connection()->hasXFixes()) { const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; - xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask); - xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask); + xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(), + XCB_ATOM_PRIMARY, mask); + xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(), + atom(QXcbAtom::AtomCLIPBOARD), mask); } // xcb_change_property_request_t and xcb_get_property_request_t are the same size @@ -253,19 +222,21 @@ QXcbClipboard::~QXcbClipboard() m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) { // First we check if there is a clipboard manager. - auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER)); - if (reply && reply->owner != XCB_NONE) { + if (connection()->selectionOwner(atom(QXcbAtom::AtomCLIPBOARD_MANAGER)) != 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()); + xcb_delete_property(xcb_connection(), connection()->qtSelectionOwner(), + atom(QXcbAtom::Atom_QT_SELECTION)); + xcb_convert_selection(xcb_connection(), connection()->qtSelectionOwner(), + atom(QXcbAtom::AtomCLIPBOARD_MANAGER), atom(QXcbAtom::AtomSAVE_TARGETS), + atom(QXcbAtom::Atom_QT_SELECTION), connection()->time()); connection()->sync(); // waiting until the clipboard manager fetches the content. - if (auto event = waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, true)) { + if (auto event = waitForClipboardEvent(connection()->qtSelectionOwner(), + XCB_SELECTION_NOTIFY, true)) { free(event); } else { - qWarning("QXcbClipboard: Unable to receive an event from the " + qCWarning(lcQpaClipboard, "QXcbClipboard: Unable to receive an event from the " "clipboard manager in a reasonable time"); } } @@ -289,15 +260,10 @@ bool QXcbClipboard::handlePropertyNotify(const xcb_generic_event_t *event) return (*it)->updateIncrementalProperty(propertyNotify); } -xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const -{ - return connection()->getSelectionOwner(atom); -} - xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const { if (mode == QClipboard::Clipboard) - return atom(QXcbAtom::CLIPBOARD); + return atom(QXcbAtom::AtomCLIPBOARD); if (mode == QClipboard::Selection) return XCB_ATOM_PRIMARY; return XCB_NONE; @@ -307,7 +273,7 @@ QClipboard::Mode QXcbClipboard::modeForAtom(xcb_atom_t a) const { if (a == XCB_ATOM_PRIMARY) return QClipboard::Selection; - if (a == atom(QXcbAtom::CLIPBOARD)) + if (a == atom(QXcbAtom::AtomCLIPBOARD)) return QClipboard::Clipboard; // not supported enum value, used to detect errors return QClipboard::FindBuffer; @@ -319,8 +285,8 @@ QMimeData * QXcbClipboard::mimeData(QClipboard::Mode mode) if (mode > QClipboard::Selection) return nullptr; - xcb_window_t clipboardOwner = getSelectionOwner(atomForMode(mode)); - if (clipboardOwner == owner()) { + xcb_window_t clipboardOwner = connection()->selectionOwner(atomForMode(mode)); + if (clipboardOwner == connection()->qtSelectionOwner()) { return m_clientClipboard[mode]; } else { if (!m_xClipboard[mode]) @@ -362,7 +328,7 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) connection()->setTime(connection()->getTimestamp()); if (data) { - newOwner = owner(); + newOwner = connection()->qtSelectionOwner(); m_clientClipboard[mode] = data; m_timestamp[mode] = connection()->time(); @@ -370,8 +336,8 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) xcb_set_selection_owner(xcb_connection(), newOwner, modeAtom, connection()->time()); - if (getSelectionOwner(modeAtom) != newOwner) { - qWarning("QXcbClipboard::setMimeData: Cannot set X11 selection owner"); + if (connection()->selectionOwner(modeAtom) != newOwner) { + qCWarning(lcQpaClipboard, "QXcbClipboard::setMimeData: Cannot set X11 selection owner"); } emitChanged(mode); @@ -386,10 +352,11 @@ bool QXcbClipboard::supportsMode(QClipboard::Mode mode) const bool QXcbClipboard::ownsMode(QClipboard::Mode mode) const { - if (m_owner == XCB_NONE || mode > QClipboard::Selection) + if (connection()->qtSelectionOwner() == XCB_NONE || mode > QClipboard::Selection) return false; - Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME || getSelectionOwner(atomForMode(mode)) == m_owner); + Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME + || connection()->selectionOwner(atomForMode(mode)) == connection()->qtSelectionOwner()); return m_timestamp[mode] != XCB_CURRENT_TIME; } @@ -438,11 +405,6 @@ void QXcbClipboard::setRequestor(xcb_window_t window) m_requestor = window; } -xcb_window_t QXcbClipboard::owner() const -{ - return m_owner; -} - xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property) { QList<xcb_atom_t> types; @@ -454,10 +416,10 @@ xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window types.append(atoms.at(j)); } } - types.append(atom(QXcbAtom::TARGETS)); - types.append(atom(QXcbAtom::MULTIPLE)); - types.append(atom(QXcbAtom::TIMESTAMP)); - types.append(atom(QXcbAtom::SAVE_TARGETS)); + types.append(atom(QXcbAtom::AtomTARGETS)); + types.append(atom(QXcbAtom::AtomMULTIPLE)); + types.append(atom(QXcbAtom::AtomTIMESTAMP)); + types.append(atom(QXcbAtom::AtomSAVE_TARGETS)); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, XCB_ATOM_ATOM, 32, types.size(), (const void *)types.constData()); @@ -481,7 +443,7 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win // don't allow INCR transfers when using MULTIPLE or to // Motif clients (since Motif doesn't support INCR) - static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::CLIP_TEMPORARY); + static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::AtomCLIP_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) @@ -490,7 +452,7 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win if (data.size() > m_maxPropertyRequestDataBytes && 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); + atom(QXcbAtom::AtomINCR), 32, 1, (const void *)&bytes); auto transaction = new QXcbClipboardTransaction(this, window, property, data, atomFormat, dataFormat); m_transactions.insert(window, transaction); return property; @@ -521,7 +483,7 @@ void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *eve // XGetSelectionOwner(dpy, XA_PRIMARY), // xevent->xselectionclear.time, d->timestamp); - xcb_window_t newOwner = getSelectionOwner(event->selection); + xcb_window_t newOwner = connection()->selectionOwner(event->selection); /* If selection ownership was given up voluntarily from QClipboard::clear(), then we do nothing here since its already handled in setMimeData. Otherwise, the event must have come from another client @@ -538,7 +500,7 @@ void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *eve void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) { if (requestor() && req->requestor == requestor()) { - qWarning("QXcbClipboard: Selection request should be caught before"); + qCWarning(lcQpaClipboard, "QXcbClipboard: Selection request should be caught before"); return; } @@ -553,7 +515,8 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) QMimeData *d; QClipboard::Mode mode = modeForAtom(req->selection); if (mode > QClipboard::Selection) { - qWarning() << "QXcbClipboard: Unknown selection" << connection()->atomName(req->selection); + qCWarning(lcQpaClipboard, "QXcbClipboard: Unknown selection %s", + connection()->atomName(req->selection).constData()); xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); return; } @@ -561,21 +524,21 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) d = m_clientClipboard[mode]; if (!d) { - qWarning("QXcbClipboard: Cannot transfer data, no data available"); + qCWarning(lcQpaClipboard, "QXcbClipboard: 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])) { - qWarning("QXcbClipboard: SelectionRequest too old"); + qCDebug(lcQpaClipboard, "QXcbClipboard: SelectionRequest too old"); xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); return; } - xcb_atom_t targetsAtom = atom(QXcbAtom::TARGETS); - xcb_atom_t multipleAtom = atom(QXcbAtom::MULTIPLE); - xcb_atom_t timestampAtom = atom(QXcbAtom::TIMESTAMP); + xcb_atom_t targetsAtom = atom(QXcbAtom::AtomTARGETS); + xcb_atom_t multipleAtom = atom(QXcbAtom::AtomMULTIPLE); + xcb_atom_t timestampAtom = atom(QXcbAtom::AtomTIMESTAMP); struct AtomPair { xcb_atom_t target; xcb_atom_t property; } *multi = nullptr; xcb_atom_t multi_type = XCB_NONE; @@ -623,7 +586,7 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) property, XCB_ATOM_INTEGER, 32, 1, &m_timestamp[mode]); ret = property; } else { - qWarning("QXcbClipboard: Invalid data timestamp"); + qCWarning(lcQpaClipboard, "QXcbClipboard: Invalid data timestamp"); } } else if (target == targetsAtom) { ret = sendTargetsSelection(d, req->requestor, property); @@ -667,7 +630,7 @@ void QXcbClipboard::handleXFixesSelectionRequest(xcb_xfixes_selection_notify_eve // Note1: Here we care only about the xfixes events that come from other processes. // Note2: If the QClipboard::clear() is issued, event->owner is XCB_NONE, // so we check selection_timestamp to not handle our own QClipboard::clear(). - if (event->owner != owner() && event->selection_timestamp > m_timestamp[mode]) { + if (event->owner != connection()->qtSelectionOwner() && event->selection_timestamp > m_timestamp[mode]) { if (!m_xClipboard[mode]) { m_xClipboard[mode].reset(new QXcbClipboardMime(mode, this)); } else { @@ -728,7 +691,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, // recover -- this shouldn't normally happen, but it doesn't // hurt to be defensive if ((int)(buffer_offset + length) > buffer->size()) { - qWarning("QXcbClipboard: buffer overflow"); + qCWarning(lcQpaClipboard, "QXcbClipboard: buffer overflow"); length = buffer->size() - buffer_offset; // escape loop @@ -749,7 +712,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, // correct size, not 0-term. if (size) *size = buffer_offset; - if (*type == atom(QXcbAtom::INCR)) + if (*type == atom(QXcbAtom::AtomINCR)) m_incr_receive_time = connection()->getTimestamp(); if (deleteProperty) xcb_delete_property(xcb_connection(), win, property); @@ -788,13 +751,12 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i const QXcbEventNode *flushedTailNode = queue->flushedTail(); if (checkManager) { - auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER)); - if (!reply || reply->owner == XCB_NONE) + if (connection()->selectionOwner(atom(QXcbAtom::AtomCLIPBOARD_MANAGER)) == XCB_NONE) return nullptr; } // process other clipboard events, since someone is probably requesting data from us - auto clipboardAtom = atom(QXcbAtom::CLIPBOARD); + auto clipboardAtom = atom(QXcbAtom::AtomCLIPBOARD); e = queue->peek([clipboardAtom](xcb_generic_event_t *event, int type) { xcb_atom_t selection = XCB_ATOM_NONE; if (type == XCB_SELECTION_REQUEST) @@ -818,7 +780,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i return nullptr; } -QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm) +std::optional<QByteArray> QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm) { QByteArray buf; QByteArray tmp_buf; @@ -835,6 +797,8 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb alloc_error = buf.size() != nbytes+1; } + QElapsedTimer timer; + timer.start(); for (;;) { connection()->flush(); xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY); @@ -870,26 +834,27 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb tmp_buf.resize(0); offset += length; } - } else { - break; } + + const auto elapsed = timer.elapsed(); + if (elapsed > clipboard_timeout) + break; } // timed out ... create a new requestor window, otherwise the requestor // could consider next request to be still part of this timed out request setRequestor(0); - return QByteArray(); + return std::nullopt; } -QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom) +std::optional<QByteArray> QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom) { - return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::_QT_SELECTION)); + return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::Atom_QT_SELECTION)); } -QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t time) +std::optional<QByteArray> QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t time) { - QByteArray buf; xcb_window_t win = requestor(); if (time == 0) time = connection()->time(); @@ -904,21 +869,24 @@ QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, free(ge); if (no_selection) - return buf; + return std::nullopt; xcb_atom_t type; + QByteArray buf; if (clipboardReadProperty(win, property, true, &buf, nullptr, &type, nullptr)) { - if (type == atom(QXcbAtom::INCR)) { + if (type == atom(QXcbAtom::AtomINCR)) { int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0; - buf = clipboardReadIncrementalProperty(win, property, nbytes, false); + return clipboardReadIncrementalProperty(win, property, nbytes, false); } + return buf; } - return buf; + return std::nullopt; } #endif // QT_NO_CLIPBOARD QT_END_NAMESPACE +#include "moc_qxcbclipboard.cpp" #include "qxcbclipboard.moc" |