File indexing completed on 2024-11-10 04:57:38
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 "clipboard.h" 0010 0011 #include "datasource.h" 0012 #include "selection_source.h" 0013 0014 #include "wayland/seat.h" 0015 #include "wayland_server.h" 0016 #include "workspace.h" 0017 #include "x11window.h" 0018 0019 #include <xcb/xcb_event.h> 0020 #include <xcb/xfixes.h> 0021 0022 #include <xwayland_logging.h> 0023 0024 namespace KWin 0025 { 0026 namespace Xwl 0027 { 0028 0029 Clipboard::Clipboard(xcb_atom_t atom, QObject *parent) 0030 : Selection(atom, parent) 0031 { 0032 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0033 0034 const uint32_t clipboardValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; 0035 xcb_create_window(xcbConn, 0036 XCB_COPY_FROM_PARENT, 0037 window(), 0038 kwinApp()->x11RootWindow(), 0039 0, 0, 0040 10, 10, 0041 0, 0042 XCB_WINDOW_CLASS_INPUT_OUTPUT, 0043 XCB_COPY_FROM_PARENT, 0044 XCB_CW_EVENT_MASK, 0045 clipboardValues); 0046 registerXfixes(); 0047 xcb_flush(xcbConn); 0048 0049 connect(waylandServer()->seat(), &SeatInterface::selectionChanged, 0050 this, &Clipboard::wlSelectionChanged); 0051 } 0052 0053 void Clipboard::wlSelectionChanged(AbstractDataSource *dsi) 0054 { 0055 if (m_waitingForTargets) { 0056 return; 0057 } 0058 0059 if (!ownsSelection(dsi)) { 0060 // Wayland native window provides new selection 0061 if (!m_checkConnection) { 0062 m_checkConnection = connect(workspace(), &Workspace::windowActivated, 0063 this, &Clipboard::checkWlSource); 0064 } 0065 // remove previous source so checkWlSource() can create a new one 0066 setWlSource(nullptr); 0067 } 0068 checkWlSource(); 0069 } 0070 0071 bool Clipboard::ownsSelection(AbstractDataSource *dsi) const 0072 { 0073 return dsi && dsi == m_selectionSource.get(); 0074 } 0075 0076 void Clipboard::checkWlSource() 0077 { 0078 if (m_waitingForTargets) { 0079 return; 0080 } 0081 0082 auto dsi = waylandServer()->seat()->selection(); 0083 auto removeSource = [this] { 0084 if (wlSource()) { 0085 setWlSource(nullptr); 0086 ownSelection(false); 0087 } 0088 }; 0089 0090 // Wayland source gets created when: 0091 // - the Wl selection exists, 0092 // - its source is not Xwayland, 0093 // - a window is active, 0094 // - this window is an Xwayland one. 0095 // 0096 // Otherwise the Wayland source gets destroyed to shield 0097 // against snooping X windows. 0098 0099 if (!dsi || ownsSelection(dsi)) { 0100 // Xwayland source or no source 0101 disconnect(m_checkConnection); 0102 m_checkConnection = QMetaObject::Connection(); 0103 removeSource(); 0104 return; 0105 } 0106 if (!qobject_cast<KWin::X11Window *>(workspace()->activeWindow())) { 0107 // no active window or active window is Wayland native 0108 removeSource(); 0109 return; 0110 } 0111 // Xwayland window is active and we need a Wayland source 0112 if (wlSource()) { 0113 // source already exists, nothing more to do 0114 return; 0115 } 0116 auto *wls = new WlSource(this); 0117 setWlSource(wls); 0118 if (dsi) { 0119 wls->setDataSourceIface(dsi); 0120 } 0121 ownSelection(true); 0122 } 0123 0124 void Clipboard::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) 0125 { 0126 const Window *window = workspace()->activeWindow(); 0127 if (!qobject_cast<const X11Window *>(window)) { 0128 // clipboard is only allowed to be acquired when Xwayland has focus 0129 // TODO: can we make this stronger (window id comparison)? 0130 createX11Source(nullptr); 0131 return; 0132 } 0133 0134 createX11Source(event); 0135 0136 if (X11Source *source = x11Source()) { 0137 source->getTargets(); 0138 m_waitingForTargets = true; 0139 } else { 0140 qCWarning(KWIN_XWL) << "Could not create a source from" << event << Qt::hex << (event ? event->owner : -1); 0141 } 0142 } 0143 0144 void Clipboard::x11OfferLost() 0145 { 0146 m_selectionSource.reset(); 0147 } 0148 0149 void Clipboard::x11OffersChanged(const QStringList &added, const QStringList &removed) 0150 { 0151 m_waitingForTargets = false; 0152 X11Source *source = x11Source(); 0153 if (!source) { 0154 qCWarning(KWIN_XWL) << "offers changed when not having an X11Source!?"; 0155 return; 0156 } 0157 0158 const Mimes offers = source->offers(); 0159 0160 if (!offers.isEmpty()) { 0161 QStringList mimeTypes; 0162 mimeTypes.reserve(offers.size()); 0163 std::transform(offers.begin(), offers.end(), std::back_inserter(mimeTypes), [](const Mimes::value_type &pair) { 0164 return pair.first; 0165 }); 0166 auto newSelection = std::make_unique<XwlDataSource>(); 0167 newSelection->setMimeTypes(mimeTypes); 0168 connect(newSelection.get(), &XwlDataSource::dataRequested, source, &X11Source::startTransfer); 0169 // we keep the old selection around because setSelection needs it to be still alive 0170 std::swap(m_selectionSource, newSelection); 0171 waylandServer()->seat()->setSelection(m_selectionSource.get()); 0172 } else { 0173 AbstractDataSource *currentSelection = waylandServer()->seat()->selection(); 0174 if (!ownsSelection(currentSelection)) { 0175 waylandServer()->seat()->setSelection(nullptr); 0176 m_selectionSource.reset(); 0177 } 0178 } 0179 } 0180 0181 } // namespace Xwl 0182 } // namespace KWin 0183 0184 #include "moc_clipboard.cpp"