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