File indexing completed on 2024-11-10 04:57:40
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "transfer.h" 0010 0011 #include "databridge.h" 0012 #include "xwayland.h" 0013 0014 #include "atoms.h" 0015 #include "wayland/datadevice.h" 0016 #include "wayland/datasource.h" 0017 #include "wayland/seat.h" 0018 #include "wayland_server.h" 0019 #include "window.h" 0020 #include "workspace.h" 0021 0022 #include <xcb/xcb_event.h> 0023 #include <xcb/xfixes.h> 0024 0025 #include <algorithm> 0026 #include <unistd.h> 0027 0028 #include <xwayland_logging.h> 0029 0030 namespace KWin 0031 { 0032 namespace Xwl 0033 { 0034 0035 // in Bytes: equals 64KB 0036 static const uint32_t s_incrChunkSize = 63 * 1024; 0037 0038 Transfer::Transfer(xcb_atom_t selection, qint32 fd, xcb_timestamp_t timestamp, QObject *parent) 0039 : QObject(parent) 0040 , m_atom(selection) 0041 , m_fd(fd) 0042 , m_timestamp(timestamp) 0043 { 0044 } 0045 0046 void Transfer::createSocketNotifier(QSocketNotifier::Type type) 0047 { 0048 delete m_notifier; 0049 m_notifier = new QSocketNotifier(m_fd, type, this); 0050 } 0051 0052 void Transfer::clearSocketNotifier() 0053 { 0054 delete m_notifier; 0055 m_notifier = nullptr; 0056 } 0057 0058 void Transfer::timeout() 0059 { 0060 if (m_timeout) { 0061 endTransfer(); 0062 } 0063 m_timeout = true; 0064 } 0065 0066 void Transfer::endTransfer() 0067 { 0068 clearSocketNotifier(); 0069 closeFd(); 0070 Q_EMIT finished(); 0071 } 0072 0073 void Transfer::closeFd() 0074 { 0075 if (m_fd < 0) { 0076 return; 0077 } 0078 close(m_fd); 0079 m_fd = -1; 0080 } 0081 0082 TransferWltoX::TransferWltoX(xcb_atom_t selection, xcb_selection_request_event_t *request, 0083 qint32 fd, QObject *parent) 0084 : Transfer(selection, fd, 0, parent) 0085 , m_request(request) 0086 { 0087 } 0088 0089 TransferWltoX::~TransferWltoX() 0090 { 0091 delete m_request; 0092 m_request = nullptr; 0093 } 0094 0095 void TransferWltoX::startTransferFromSource() 0096 { 0097 createSocketNotifier(QSocketNotifier::Read); 0098 connect(socketNotifier(), &QSocketNotifier::activated, this, [this](int socket) { 0099 readWlSource(); 0100 }); 0101 } 0102 0103 int TransferWltoX::flushSourceData() 0104 { 0105 Q_ASSERT(!m_chunks.isEmpty()); 0106 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0107 0108 xcb_change_property(xcbConn, 0109 XCB_PROP_MODE_REPLACE, 0110 m_request->requestor, 0111 m_request->property, 0112 m_request->target, 0113 8, 0114 m_chunks.first().first.size(), 0115 m_chunks.first().first.data()); 0116 xcb_flush(xcbConn); 0117 0118 m_propertyIsSet = true; 0119 resetTimeout(); 0120 0121 const auto rm = m_chunks.takeFirst(); 0122 return rm.first.size(); 0123 } 0124 0125 void TransferWltoX::startIncr() 0126 { 0127 Q_ASSERT(m_chunks.size() == 1); 0128 0129 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0130 0131 uint32_t mask[] = {XCB_EVENT_MASK_PROPERTY_CHANGE}; 0132 xcb_change_window_attributes(xcbConn, 0133 m_request->requestor, 0134 XCB_CW_EVENT_MASK, mask); 0135 0136 // spec says to make the available space larger 0137 const uint32_t chunkSpace = 1024 + s_incrChunkSize; 0138 xcb_change_property(xcbConn, 0139 XCB_PROP_MODE_REPLACE, 0140 m_request->requestor, 0141 m_request->property, 0142 atoms->incr, 0143 32, 1, &chunkSpace); 0144 xcb_flush(xcbConn); 0145 0146 setIncr(true); 0147 // first data will be flushed after the property has been deleted 0148 // again by the requestor 0149 m_flushPropertyOnDelete = true; 0150 m_propertyIsSet = true; 0151 Q_EMIT selectionNotify(m_request, true); 0152 } 0153 0154 void TransferWltoX::readWlSource() 0155 { 0156 if (m_chunks.size() == 0 || m_chunks.last().second == s_incrChunkSize) { 0157 // append new chunk 0158 auto next = QPair<QByteArray, int>(); 0159 next.first.resize(s_incrChunkSize); 0160 next.second = 0; 0161 m_chunks.append(next); 0162 } 0163 0164 const auto oldLen = m_chunks.last().second; 0165 const auto avail = s_incrChunkSize - m_chunks.last().second; 0166 Q_ASSERT(avail > 0); 0167 0168 ssize_t readLen = read(fd(), m_chunks.last().first.data() + oldLen, avail); 0169 if (readLen == -1) { 0170 qCWarning(KWIN_XWL) << "Error reading in Wl data."; 0171 0172 // TODO: cleanup X side? 0173 endTransfer(); 0174 return; 0175 } 0176 m_chunks.last().second = oldLen + readLen; 0177 0178 if (readLen == 0) { 0179 // at the fd end - complete transfer now 0180 m_chunks.last().first.resize(m_chunks.last().second); 0181 0182 if (incr()) { 0183 // incremental transfer is to be completed now 0184 m_flushPropertyOnDelete = true; 0185 if (!m_propertyIsSet) { 0186 // flush if target's property is not set at the moment 0187 flushSourceData(); 0188 } 0189 clearSocketNotifier(); 0190 } else { 0191 // non incremental transfer is to be completed now, 0192 // data can be transferred to X client via a single property set 0193 flushSourceData(); 0194 Q_EMIT selectionNotify(m_request, true); 0195 endTransfer(); 0196 } 0197 } else if (m_chunks.last().second == s_incrChunkSize) { 0198 // first chunk full, but not yet at fd end -> go incremental 0199 if (incr()) { 0200 m_flushPropertyOnDelete = true; 0201 if (!m_propertyIsSet) { 0202 // flush if target's property is not set at the moment 0203 flushSourceData(); 0204 } 0205 } else { 0206 // starting incremental transfer 0207 startIncr(); 0208 } 0209 } 0210 resetTimeout(); 0211 } 0212 0213 bool TransferWltoX::handlePropertyNotify(xcb_property_notify_event_t *event) 0214 { 0215 if (event->window == m_request->requestor) { 0216 if (event->state == XCB_PROPERTY_DELETE && event->atom == m_request->property) { 0217 handlePropertyDelete(); 0218 } 0219 return true; 0220 } 0221 return false; 0222 } 0223 0224 void TransferWltoX::handlePropertyDelete() 0225 { 0226 if (!incr()) { 0227 // non-incremental transfer: nothing to do 0228 return; 0229 } 0230 m_propertyIsSet = false; 0231 0232 if (m_flushPropertyOnDelete) { 0233 if (!socketNotifier() && m_chunks.isEmpty()) { 0234 // transfer complete 0235 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0236 0237 uint32_t mask[] = {0}; 0238 xcb_change_window_attributes(xcbConn, 0239 m_request->requestor, 0240 XCB_CW_EVENT_MASK, mask); 0241 0242 xcb_change_property(xcbConn, 0243 XCB_PROP_MODE_REPLACE, 0244 m_request->requestor, 0245 m_request->property, 0246 m_request->target, 0247 8, 0, nullptr); 0248 xcb_flush(xcbConn); 0249 m_flushPropertyOnDelete = false; 0250 endTransfer(); 0251 } else if (!m_chunks.isEmpty()) { 0252 flushSourceData(); 0253 } 0254 } 0255 } 0256 0257 TransferXtoWl::TransferXtoWl(xcb_atom_t selection, xcb_atom_t target, qint32 fd, 0258 xcb_timestamp_t timestamp, xcb_window_t parentWindow, 0259 QObject *parent) 0260 : Transfer(selection, fd, timestamp, parent) 0261 { 0262 // create transfer window 0263 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0264 m_window = xcb_generate_id(xcbConn); 0265 const uint32_t values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; 0266 xcb_create_window(xcbConn, 0267 XCB_COPY_FROM_PARENT, 0268 m_window, 0269 parentWindow, 0270 0, 0, 0271 10, 10, 0272 0, 0273 XCB_WINDOW_CLASS_INPUT_OUTPUT, 0274 XCB_COPY_FROM_PARENT, 0275 XCB_CW_EVENT_MASK, 0276 values); 0277 // convert selection 0278 xcb_convert_selection(xcbConn, 0279 m_window, 0280 selection, 0281 target, 0282 atoms->wl_selection, 0283 timestamp); 0284 xcb_flush(xcbConn); 0285 } 0286 0287 TransferXtoWl::~TransferXtoWl() 0288 { 0289 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0290 xcb_destroy_window(xcbConn, m_window); 0291 xcb_flush(xcbConn); 0292 0293 delete m_receiver; 0294 m_receiver = nullptr; 0295 } 0296 0297 bool TransferXtoWl::handlePropertyNotify(xcb_property_notify_event_t *event) 0298 { 0299 if (event->window == m_window) { 0300 if (event->state == XCB_PROPERTY_NEW_VALUE && event->atom == atoms->wl_selection) { 0301 getIncrChunk(); 0302 } 0303 return true; 0304 } 0305 return false; 0306 } 0307 0308 bool TransferXtoWl::handleSelectionNotify(xcb_selection_notify_event_t *event) 0309 { 0310 if (event->requestor != m_window) { 0311 return false; 0312 } 0313 if (event->selection != atom()) { 0314 return false; 0315 } 0316 if (event->property == XCB_ATOM_NONE) { 0317 qCWarning(KWIN_XWL) << "Incoming X selection conversion failed"; 0318 return true; 0319 } 0320 if (event->target == atoms->targets) { 0321 qCWarning(KWIN_XWL) << "Received targets too late"; 0322 // TODO: or allow it? 0323 return true; 0324 } 0325 if (m_receiver) { 0326 // second selection notify element - misbehaving source 0327 0328 // TODO: cancel this transfer? 0329 return true; 0330 } 0331 0332 m_receiver = new DataReceiver; 0333 0334 startTransfer(); 0335 return true; 0336 } 0337 0338 void TransferXtoWl::startTransfer() 0339 { 0340 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0341 auto cookie = xcb_get_property(xcbConn, 0342 1, 0343 m_window, 0344 atoms->wl_selection, 0345 XCB_GET_PROPERTY_TYPE_ANY, 0346 0, 0347 0x1fffffff); 0348 0349 auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); 0350 if (reply == nullptr) { 0351 qCWarning(KWIN_XWL) << "Can't get selection property."; 0352 endTransfer(); 0353 return; 0354 } 0355 0356 if (reply->type == atoms->incr) { 0357 setIncr(true); 0358 free(reply); 0359 } else { 0360 setIncr(false); 0361 // reply's ownership is transferred 0362 m_receiver->transferFromProperty(reply); 0363 dataSourceWrite(); 0364 } 0365 } 0366 0367 void TransferXtoWl::getIncrChunk() 0368 { 0369 if (!incr()) { 0370 // source tries to sent incrementally, but did not announce it before 0371 return; 0372 } 0373 if (!m_receiver) { 0374 // receive mechanism has not yet been setup 0375 return; 0376 } 0377 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0378 0379 auto cookie = xcb_get_property(xcbConn, 0380 0, 0381 m_window, 0382 atoms->wl_selection, 0383 XCB_GET_PROPERTY_TYPE_ANY, 0384 0, 0385 0x1fffffff); 0386 0387 auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); 0388 if (!reply) { 0389 qCWarning(KWIN_XWL) << "Can't get selection property."; 0390 endTransfer(); 0391 return; 0392 } 0393 0394 if (xcb_get_property_value_length(reply) > 0) { 0395 // reply's ownership is transferred 0396 m_receiver->transferFromProperty(reply); 0397 dataSourceWrite(); 0398 } else { 0399 // Transfer complete 0400 free(reply); 0401 endTransfer(); 0402 } 0403 } 0404 0405 DataReceiver::~DataReceiver() 0406 { 0407 if (m_propertyReply) { 0408 free(m_propertyReply); 0409 m_propertyReply = nullptr; 0410 } 0411 } 0412 0413 void DataReceiver::transferFromProperty(xcb_get_property_reply_t *reply) 0414 { 0415 m_propertyStart = 0; 0416 m_propertyReply = reply; 0417 0418 setData(static_cast<char *>(xcb_get_property_value(reply)), 0419 xcb_get_property_value_length(reply)); 0420 } 0421 0422 void DataReceiver::setData(const char *value, int length) 0423 { 0424 // simply set data without copy 0425 m_data = QByteArray::fromRawData(value, length); 0426 } 0427 0428 QByteArray DataReceiver::data() const 0429 { 0430 return QByteArray::fromRawData(m_data.data() + m_propertyStart, 0431 m_data.size() - m_propertyStart); 0432 } 0433 0434 void DataReceiver::partRead(int length) 0435 { 0436 m_propertyStart += length; 0437 if (m_propertyStart == m_data.size()) { 0438 Q_ASSERT(m_propertyReply); 0439 free(m_propertyReply); 0440 m_propertyReply = nullptr; 0441 } 0442 } 0443 0444 void TransferXtoWl::dataSourceWrite() 0445 { 0446 QByteArray property = m_receiver->data(); 0447 0448 ssize_t len = write(fd(), property.constData(), property.size()); 0449 if (len == -1) { 0450 qCWarning(KWIN_XWL) << "X11 to Wayland write error on fd:" << fd(); 0451 endTransfer(); 0452 return; 0453 } 0454 0455 m_receiver->partRead(len); 0456 if (len == property.size()) { 0457 // property completely transferred 0458 if (incr()) { 0459 clearSocketNotifier(); 0460 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0461 xcb_delete_property(xcbConn, 0462 m_window, 0463 atoms->wl_selection); 0464 xcb_flush(xcbConn); 0465 } else { 0466 // transfer complete 0467 endTransfer(); 0468 } 0469 } else { 0470 if (!socketNotifier()) { 0471 createSocketNotifier(QSocketNotifier::Write); 0472 connect(socketNotifier(), &QSocketNotifier::activated, this, [this](int socket) { 0473 dataSourceWrite(); 0474 }); 0475 } 0476 } 0477 resetTimeout(); 0478 } 0479 0480 } // namespace Xwl 0481 } // namespace KWin 0482 0483 #include "moc_transfer.cpp"