File indexing completed on 2025-03-16 11:21:57
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "selection.h" 0010 #include "databridge.h" 0011 #include "selection_source.h" 0012 #include "transfer.h" 0013 0014 #include "atoms.h" 0015 #include "utils/xcbutils.h" 0016 #include "workspace.h" 0017 #include "x11window.h" 0018 0019 #include <xcb/xcb_event.h> 0020 #include <xcb/xfixes.h> 0021 0022 #include <QTimer> 0023 0024 namespace KWin 0025 { 0026 namespace Xwl 0027 { 0028 0029 xcb_atom_t Selection::mimeTypeToAtom(const QString &mimeType) 0030 { 0031 if (mimeType == QLatin1String("text/plain;charset=utf-8")) { 0032 return atoms->utf8_string; 0033 } 0034 if (mimeType == QLatin1String("text/plain")) { 0035 return atoms->text; 0036 } 0037 if (mimeType == QLatin1String("text/x-uri")) { 0038 return atoms->uri_list; 0039 } 0040 return mimeTypeToAtomLiteral(mimeType); 0041 } 0042 0043 xcb_atom_t Selection::mimeTypeToAtomLiteral(const QString &mimeType) 0044 { 0045 return Xcb::Atom(mimeType.toLatin1(), false, kwinApp()->x11Connection()); 0046 } 0047 0048 QString Selection::atomName(xcb_atom_t atom) 0049 { 0050 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0051 xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom); 0052 xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr); 0053 if (!nameReply) { 0054 return QString(); 0055 } 0056 0057 const size_t length = xcb_get_atom_name_name_length(nameReply); 0058 QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length); 0059 free(nameReply); 0060 return name; 0061 } 0062 0063 QStringList Selection::atomToMimeTypes(xcb_atom_t atom) 0064 { 0065 QStringList mimeTypes; 0066 0067 if (atom == atoms->utf8_string) { 0068 mimeTypes << QString::fromLatin1("text/plain;charset=utf-8"); 0069 } else if (atom == atoms->text) { 0070 mimeTypes << QString::fromLatin1("text/plain"); 0071 } else if (atom == atoms->uri_list) { 0072 mimeTypes << "text/uri-list" 0073 << "text/x-uri"; 0074 } else { 0075 mimeTypes << atomName(atom); 0076 } 0077 return mimeTypes; 0078 } 0079 0080 Selection::Selection(xcb_atom_t atom, QObject *parent) 0081 : QObject(parent) 0082 , m_atom(atom) 0083 { 0084 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0085 m_window = xcb_generate_id(kwinApp()->x11Connection()); 0086 m_requestorWindow = m_window; 0087 xcb_flush(xcbConn); 0088 } 0089 0090 bool Selection::handleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) 0091 { 0092 if (event->window != m_window) { 0093 return false; 0094 } 0095 if (event->selection != m_atom) { 0096 return false; 0097 } 0098 if (m_disownPending) { 0099 // notify of our own disown - ignore it 0100 m_disownPending = false; 0101 return true; 0102 } 0103 if (event->owner == m_window && m_waylandSource) { 0104 // When we claim a selection we must use XCB_TIME_CURRENT, 0105 // grab the actual timestamp here to answer TIMESTAMP requests 0106 // correctly 0107 m_waylandSource->setTimestamp(event->timestamp); 0108 m_timestamp = event->timestamp; 0109 return true; 0110 } 0111 0112 // Being here means some other X window has claimed the selection. 0113 doHandleXfixesNotify(event); 0114 return true; 0115 } 0116 0117 bool Selection::filterEvent(xcb_generic_event_t *event) 0118 { 0119 switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { 0120 case XCB_SELECTION_NOTIFY: 0121 return handleSelectionNotify(reinterpret_cast<xcb_selection_notify_event_t *>(event)); 0122 case XCB_PROPERTY_NOTIFY: 0123 return handlePropertyNotify(reinterpret_cast<xcb_property_notify_event_t *>(event)); 0124 case XCB_SELECTION_REQUEST: 0125 return handleSelectionRequest(reinterpret_cast<xcb_selection_request_event_t *>(event)); 0126 case XCB_CLIENT_MESSAGE: 0127 return handleClientMessage(reinterpret_cast<xcb_client_message_event_t *>(event)); 0128 default: 0129 if (event->response_type == Xcb::Extensions::self()->fixesSelectionNotifyEvent()) { 0130 return handleXfixesNotify(reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event)); 0131 } 0132 return false; 0133 } 0134 } 0135 0136 void Selection::sendSelectionNotify(xcb_selection_request_event_t *event, bool success) 0137 { 0138 // Every X11 event is 32 bytes (see man xcb_send_event), so XCB will copy 0139 // 32 unconditionally. Use a union to ensure we don't disclose stack memory. 0140 union { 0141 xcb_selection_notify_event_t notify; 0142 char buffer[32]; 0143 } u; 0144 memset(&u, 0, sizeof(u)); 0145 static_assert(sizeof(u.notify) < 32, "wouldn't need the union otherwise"); 0146 u.notify.response_type = XCB_SELECTION_NOTIFY; 0147 u.notify.sequence = 0; 0148 u.notify.time = event->time; 0149 u.notify.requestor = event->requestor; 0150 u.notify.selection = event->selection; 0151 u.notify.target = event->target; 0152 u.notify.property = success ? event->property : xcb_atom_t(XCB_ATOM_NONE); 0153 0154 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0155 xcb_send_event(xcbConn, 0, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&u); 0156 xcb_flush(xcbConn); 0157 } 0158 0159 void Selection::registerXfixes() 0160 { 0161 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0162 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; 0163 xcb_xfixes_select_selection_input(xcbConn, 0164 m_window, 0165 m_atom, 0166 mask); 0167 xcb_flush(xcbConn); 0168 } 0169 0170 void Selection::setWlSource(WlSource *source) 0171 { 0172 if (m_waylandSource) { 0173 m_waylandSource->deleteLater(); 0174 m_waylandSource = nullptr; 0175 } 0176 delete m_xSource; 0177 m_xSource = nullptr; 0178 if (source) { 0179 m_waylandSource = source; 0180 connect(source, &WlSource::transferReady, this, &Selection::startTransferToX); 0181 } 0182 } 0183 0184 void Selection::createX11Source(xcb_xfixes_selection_notify_event_t *event) 0185 { 0186 setWlSource(nullptr); 0187 if (!event || event->owner == XCB_WINDOW_NONE) { 0188 return; 0189 } 0190 m_xSource = new X11Source(this, event); 0191 0192 connect(m_xSource, &X11Source::offersChanged, this, &Selection::x11OffersChanged); 0193 connect(m_xSource, &X11Source::transferReady, this, &Selection::startTransferToWayland); 0194 } 0195 0196 void Selection::ownSelection(bool own) 0197 { 0198 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0199 if (own) { 0200 xcb_set_selection_owner(xcbConn, 0201 m_window, 0202 m_atom, 0203 XCB_TIME_CURRENT_TIME); 0204 } else { 0205 m_disownPending = true; 0206 xcb_set_selection_owner(xcbConn, 0207 XCB_WINDOW_NONE, 0208 m_atom, 0209 m_timestamp); 0210 } 0211 xcb_flush(xcbConn); 0212 } 0213 0214 void Selection::overwriteRequestorWindow(xcb_window_t window) 0215 { 0216 Q_ASSERT(m_xSource); 0217 if (window == XCB_WINDOW_NONE) { 0218 // reset 0219 window = m_window; 0220 } 0221 m_requestorWindow = window; 0222 m_xSource->setRequestor(window); 0223 } 0224 0225 bool Selection::handleSelectionRequest(xcb_selection_request_event_t *event) 0226 { 0227 if (event->selection != m_atom) { 0228 return false; 0229 } 0230 0231 if (qobject_cast<X11Window *>(workspace()->activeWindow()) == nullptr) { 0232 // Receiving Wayland selection not allowed when no Xwayland surface active 0233 // filter the event, but don't act upon it 0234 sendSelectionNotify(event, false); 0235 return true; 0236 } 0237 0238 if (m_window != event->owner || !m_waylandSource) { 0239 if (event->time < m_timestamp) { 0240 // cancel earlier attempts at receiving a selection 0241 // TODO: is this for sure without problems? 0242 sendSelectionNotify(event, false); 0243 return true; 0244 } 0245 return false; 0246 } 0247 return m_waylandSource->handleSelectionRequest(event); 0248 } 0249 0250 bool Selection::handleSelectionNotify(xcb_selection_notify_event_t *event) 0251 { 0252 if (m_xSource && m_xSource->handleSelectionNotify(event)) { 0253 return true; 0254 } 0255 for (TransferXtoWl *transfer : std::as_const(m_xToWlTransfers)) { 0256 if (transfer->handleSelectionNotify(event)) { 0257 return true; 0258 } 0259 } 0260 return false; 0261 } 0262 0263 bool Selection::handlePropertyNotify(xcb_property_notify_event_t *event) 0264 { 0265 for (TransferXtoWl *transfer : std::as_const(m_xToWlTransfers)) { 0266 if (transfer->handlePropertyNotify(event)) { 0267 return true; 0268 } 0269 } 0270 for (TransferWltoX *transfer : std::as_const(m_wlToXTransfers)) { 0271 if (transfer->handlePropertyNotify(event)) { 0272 return true; 0273 } 0274 } 0275 return false; 0276 } 0277 0278 void Selection::startTransferToWayland(xcb_atom_t target, qint32 fd) 0279 { 0280 // create new x to wl data transfer object 0281 auto *transfer = new TransferXtoWl(m_atom, target, fd, m_xSource->timestamp(), m_requestorWindow, this); 0282 m_xToWlTransfers << transfer; 0283 0284 connect(transfer, &TransferXtoWl::finished, this, [this, transfer]() { 0285 Q_EMIT transferFinished(transfer->timestamp()); 0286 transfer->deleteLater(); 0287 m_xToWlTransfers.removeOne(transfer); 0288 endTimeoutTransfersTimer(); 0289 }); 0290 startTimeoutTransfersTimer(); 0291 } 0292 0293 void Selection::startTransferToX(xcb_selection_request_event_t *event, qint32 fd) 0294 { 0295 // create new wl to x data transfer object 0296 auto *transfer = new TransferWltoX(m_atom, event, fd, this); 0297 0298 connect(transfer, &TransferWltoX::selectionNotify, this, &Selection::sendSelectionNotify); 0299 connect(transfer, &TransferWltoX::finished, this, [this, transfer]() { 0300 Q_EMIT transferFinished(transfer->timestamp()); 0301 0302 // TODO: serialize? see comment below. 0303 // const bool wasActive = (transfer == m_wlToXTransfers[0]); 0304 transfer->deleteLater(); 0305 m_wlToXTransfers.removeOne(transfer); 0306 endTimeoutTransfersTimer(); 0307 // if (wasActive && !m_wlToXTransfers.isEmpty()) { 0308 // m_wlToXTransfers[0]->startTransferFromSource(); 0309 // } 0310 }); 0311 0312 // add it to list of queued transfers 0313 m_wlToXTransfers.append(transfer); 0314 0315 // TODO: Do we need to serialize the transfers, or can we do 0316 // them in parallel as we do it right now? 0317 transfer->startTransferFromSource(); 0318 // if (m_wlToXTransfers.size() == 1) { 0319 // transfer->startTransferFromSource(); 0320 // } 0321 startTimeoutTransfersTimer(); 0322 } 0323 0324 void Selection::startTimeoutTransfersTimer() 0325 { 0326 if (m_timeoutTransfers) { 0327 return; 0328 } 0329 m_timeoutTransfers = new QTimer(this); 0330 connect(m_timeoutTransfers, &QTimer::timeout, this, &Selection::timeoutTransfers); 0331 m_timeoutTransfers->start(5000); 0332 } 0333 0334 void Selection::endTimeoutTransfersTimer() 0335 { 0336 if (m_xToWlTransfers.isEmpty() && m_wlToXTransfers.isEmpty()) { 0337 delete m_timeoutTransfers; 0338 m_timeoutTransfers = nullptr; 0339 } 0340 } 0341 0342 void Selection::timeoutTransfers() 0343 { 0344 for (TransferXtoWl *transfer : std::as_const(m_xToWlTransfers)) { 0345 transfer->timeout(); 0346 } 0347 for (TransferWltoX *transfer : std::as_const(m_wlToXTransfers)) { 0348 transfer->timeout(); 0349 } 0350 } 0351 0352 } // namespace Xwl 0353 } // namespace KWin