File indexing completed on 2024-11-10 04:57:39
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_source.h" 0010 #include "selection.h" 0011 #include "transfer.h" 0012 0013 #include "atoms.h" 0014 #include "wayland/datadevice.h" 0015 #include "wayland/datasource.h" 0016 #include "wayland/seat.h" 0017 #include "wayland_server.h" 0018 0019 #include <fcntl.h> 0020 #include <unistd.h> 0021 0022 #include <xwayland_logging.h> 0023 0024 namespace KWin 0025 { 0026 namespace Xwl 0027 { 0028 0029 SelectionSource::SelectionSource(Selection *selection) 0030 : QObject(selection) 0031 , m_selection(selection) 0032 , m_window(selection->window()) 0033 { 0034 } 0035 0036 WlSource::WlSource(Selection *selection) 0037 : SelectionSource(selection) 0038 { 0039 } 0040 0041 void WlSource::setDataSourceIface(AbstractDataSource *dsi) 0042 { 0043 if (m_dsi == dsi) { 0044 return; 0045 } 0046 for (const auto &mime : dsi->mimeTypes()) { 0047 m_offers << mime; 0048 } 0049 0050 // TODO, this can probably be removed after some testing 0051 // all mime types should be constant after a data source is set 0052 m_offerConnection = connect(dsi, 0053 &DataSourceInterface::mimeTypeOffered, 0054 this, &WlSource::receiveOffer); 0055 0056 m_dsi = dsi; 0057 } 0058 0059 void WlSource::receiveOffer(const QString &mime) 0060 { 0061 m_offers << mime; 0062 } 0063 0064 void WlSource::sendSelectionNotify(xcb_selection_request_event_t *event, bool success) 0065 { 0066 Selection::sendSelectionNotify(event, success); 0067 } 0068 0069 bool WlSource::handleSelectionRequest(xcb_selection_request_event_t *event) 0070 { 0071 if (event->target == atoms->targets) { 0072 sendTargets(event); 0073 } else if (event->target == atoms->timestamp) { 0074 sendTimestamp(event); 0075 } else if (event->target == atoms->delete_atom) { 0076 sendSelectionNotify(event, true); 0077 } else { 0078 // try to send mime data 0079 if (!checkStartTransfer(event)) { 0080 sendSelectionNotify(event, false); 0081 } 0082 } 0083 return true; 0084 } 0085 0086 void WlSource::sendTargets(xcb_selection_request_event_t *event) 0087 { 0088 QList<xcb_atom_t> targets; 0089 targets.resize(m_offers.size() + 2); 0090 targets[0] = atoms->timestamp; 0091 targets[1] = atoms->targets; 0092 0093 size_t cnt = 2; 0094 for (const auto &mime : std::as_const(m_offers)) { 0095 targets[cnt] = Selection::mimeTypeToAtom(mime); 0096 cnt++; 0097 } 0098 0099 xcb_change_property(kwinApp()->x11Connection(), 0100 XCB_PROP_MODE_REPLACE, 0101 event->requestor, 0102 event->property, 0103 XCB_ATOM_ATOM, 0104 32, cnt, targets.data()); 0105 sendSelectionNotify(event, true); 0106 } 0107 0108 void WlSource::sendTimestamp(xcb_selection_request_event_t *event) 0109 { 0110 const xcb_timestamp_t time = timestamp(); 0111 xcb_change_property(kwinApp()->x11Connection(), 0112 XCB_PROP_MODE_REPLACE, 0113 event->requestor, 0114 event->property, 0115 XCB_ATOM_INTEGER, 0116 32, 1, &time); 0117 0118 sendSelectionNotify(event, true); 0119 } 0120 0121 bool WlSource::checkStartTransfer(xcb_selection_request_event_t *event) 0122 { 0123 // check interfaces available 0124 if (!m_dsi) { 0125 return false; 0126 } 0127 0128 const auto targets = Selection::atomToMimeTypes(event->target); 0129 if (targets.isEmpty()) { 0130 qCDebug(KWIN_XWL) << "Unknown selection atom. Ignoring request."; 0131 return false; 0132 } 0133 const auto firstTarget = targets[0]; 0134 0135 auto cmp = [firstTarget](const QString &b) { 0136 if (firstTarget == "text/uri-list") { 0137 // Wayland sources might announce the old mime or the new standard 0138 return firstTarget == b || b == "text/x-uri"; 0139 } 0140 return firstTarget == b; 0141 }; 0142 // check supported mimes 0143 const auto offers = m_dsi->mimeTypes(); 0144 const auto mimeIt = std::find_if(offers.begin(), offers.end(), cmp); 0145 if (mimeIt == offers.end()) { 0146 // Requested Mime not supported. Not sending selection. 0147 return false; 0148 } 0149 0150 int p[2]; 0151 if (pipe2(p, O_CLOEXEC) == -1) { 0152 qCWarning(KWIN_XWL) << "Pipe failed. Not sending selection."; 0153 return false; 0154 } 0155 0156 m_dsi->requestData(*mimeIt, p[1]); 0157 0158 Q_EMIT transferReady(new xcb_selection_request_event_t(*event), p[0]); 0159 return true; 0160 } 0161 0162 X11Source::X11Source(Selection *selection, xcb_xfixes_selection_notify_event_t *event) 0163 : SelectionSource(selection) 0164 { 0165 setTimestamp(event->timestamp); 0166 } 0167 0168 void X11Source::getTargets() 0169 { 0170 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0171 /* will lead to a selection request event for the new owner */ 0172 xcb_convert_selection(xcbConn, 0173 window(), 0174 selection()->atom(), 0175 atoms->targets, 0176 atoms->wl_selection, 0177 timestamp()); 0178 xcb_flush(xcbConn); 0179 } 0180 0181 using Mime = QPair<QString, xcb_atom_t>; 0182 0183 void X11Source::handleTargets() 0184 { 0185 // receive targets 0186 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0187 xcb_get_property_cookie_t cookie = xcb_get_property(xcbConn, 0188 1, 0189 window(), 0190 atoms->wl_selection, 0191 XCB_GET_PROPERTY_TYPE_ANY, 0192 0, 0193 4096); 0194 auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr); 0195 if (!reply) { 0196 qCDebug(KWIN_XWL) << "Failed to get selection property"; 0197 return; 0198 } 0199 if (reply->type != XCB_ATOM_ATOM) { 0200 qCDebug(KWIN_XWL) << "Wrong reply type"; 0201 free(reply); 0202 return; 0203 } 0204 0205 QStringList added; 0206 QStringList removed; 0207 0208 Mimes all; 0209 xcb_atom_t *value = static_cast<xcb_atom_t *>(xcb_get_property_value(reply)); 0210 for (uint32_t i = 0; i < reply->value_len; i++) { 0211 if (value[i] == XCB_ATOM_NONE) { 0212 continue; 0213 } 0214 0215 const auto mimeStrings = Selection::atomToMimeTypes(value[i]); 0216 if (mimeStrings.isEmpty()) { 0217 // TODO: this should never happen? assert? 0218 continue; 0219 } 0220 0221 const auto mimeIt = std::find_if(m_offers.begin(), m_offers.end(), 0222 [value, i](const Mime &mime) { 0223 return mime.second == value[i]; 0224 }); 0225 0226 auto mimePair = Mime(mimeStrings[0], value[i]); 0227 if (mimeIt == m_offers.end()) { 0228 added << mimePair.first; 0229 } else { 0230 m_offers.removeAll(mimePair); 0231 } 0232 all << mimePair; 0233 } 0234 // all left in m_offers are not in the updated targets 0235 for (const auto &mimePair : std::as_const(m_offers)) { 0236 removed << mimePair.first; 0237 } 0238 m_offers = all; 0239 0240 if (!added.isEmpty() || !removed.isEmpty()) { 0241 Q_EMIT offersChanged(added, removed); 0242 } 0243 0244 free(reply); 0245 } 0246 0247 void X11Source::setOffers(const Mimes &offers) 0248 { 0249 m_offers = offers; 0250 } 0251 0252 bool X11Source::handleSelectionNotify(xcb_selection_notify_event_t *event) 0253 { 0254 if (event->requestor != window()) { 0255 return false; 0256 } 0257 if (event->selection != selection()->atom()) { 0258 return false; 0259 } 0260 if (event->property == XCB_ATOM_NONE) { 0261 qCWarning(KWIN_XWL) << "Incoming X selection conversion failed"; 0262 return true; 0263 } 0264 if (event->target == atoms->targets) { 0265 handleTargets(); 0266 return true; 0267 } 0268 return false; 0269 } 0270 0271 void X11Source::startTransfer(const QString &mimeName, qint32 fd) 0272 { 0273 const auto mimeIt = std::find_if(m_offers.begin(), m_offers.end(), 0274 [mimeName](const Mime &mime) { 0275 return mime.first == mimeName; 0276 }); 0277 if (mimeIt == m_offers.end()) { 0278 qCDebug(KWIN_XWL) << "Sending X11 clipboard to Wayland failed: unsupported MIME."; 0279 close(fd); 0280 return; 0281 } 0282 0283 Q_EMIT transferReady((*mimeIt).second, fd); 0284 } 0285 0286 } // namespace Xwl 0287 } // namespace KWin 0288 0289 #include "moc_selection_source.cpp"