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 SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org> 0007 SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "dnd.h" 0011 0012 #include "databridge.h" 0013 #include "drag_wl.h" 0014 #include "drag_x.h" 0015 #include "selection_source.h" 0016 0017 #include "atoms.h" 0018 #include "wayland/compositor.h" 0019 #include "wayland/datasource.h" 0020 #include "wayland/seat.h" 0021 #include "wayland_server.h" 0022 #include "window.h" 0023 #include "workspace.h" 0024 #include "xwayland.h" 0025 #include "xwldrophandler.h" 0026 0027 #include <QMouseEvent> 0028 0029 #include <xcb/xcb.h> 0030 0031 namespace KWin 0032 { 0033 namespace Xwl 0034 { 0035 0036 // version of DnD support in X 0037 const static uint32_t s_version = 5; 0038 uint32_t Dnd::version() 0039 { 0040 return s_version; 0041 } 0042 0043 XwlDropHandler *Dnd::dropHandler() const 0044 { 0045 return m_dropHandler; 0046 } 0047 0048 Dnd::Dnd(xcb_atom_t atom, QObject *parent) 0049 : Selection(atom, parent) 0050 , m_dropHandler(new XwlDropHandler(this)) 0051 { 0052 xcb_connection_t *xcbConn = kwinApp()->x11Connection(); 0053 0054 const uint32_t dndValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; 0055 xcb_create_window(xcbConn, 0056 XCB_COPY_FROM_PARENT, 0057 window(), 0058 kwinApp()->x11RootWindow(), 0059 0, 0, 0060 8192, 8192, // TODO: get current screen size and connect to changes 0061 0, 0062 XCB_WINDOW_CLASS_INPUT_OUTPUT, 0063 XCB_COPY_FROM_PARENT, 0064 XCB_CW_EVENT_MASK, 0065 dndValues); 0066 registerXfixes(); 0067 0068 xcb_change_property(xcbConn, 0069 XCB_PROP_MODE_REPLACE, 0070 window(), 0071 atoms->xdnd_aware, 0072 XCB_ATOM_ATOM, 0073 32, 1, &s_version); 0074 xcb_flush(xcbConn); 0075 0076 connect(waylandServer()->seat(), &SeatInterface::dragStarted, this, &Dnd::startDrag); 0077 connect(waylandServer()->seat(), &SeatInterface::dragEnded, this, &Dnd::endDrag); 0078 } 0079 0080 void Dnd::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) 0081 { 0082 if (qobject_cast<XToWlDrag *>(m_currentDrag)) { 0083 // X drag is in progress, rogue X client took over the selection. 0084 return; 0085 } 0086 if (m_currentDrag) { 0087 // Wl drag is in progress - don't overwrite by rogue X client, 0088 // get it back instead! 0089 ownSelection(true); 0090 return; 0091 } 0092 createX11Source(nullptr); 0093 const auto *seat = waylandServer()->seat(); 0094 auto *originSurface = seat->focusedPointerSurface(); 0095 if (!originSurface) { 0096 return; 0097 } 0098 if (originSurface->client() != waylandServer()->xWaylandConnection()) { 0099 // focused surface client is not Xwayland - do not allow drag to start 0100 // TODO: can we make this stronger (window id comparison)? 0101 return; 0102 } 0103 if (!seat->isPointerButtonPressed(Qt::LeftButton)) { 0104 // we only allow drags to be started on (left) pointer button being 0105 // pressed for now 0106 return; 0107 } 0108 createX11Source(event); 0109 X11Source *source = x11Source(); 0110 if (!source) { 0111 return; 0112 } 0113 m_currentDrag = new XToWlDrag(source, this); 0114 } 0115 0116 void Dnd::x11OfferLost() 0117 { 0118 } 0119 0120 void Dnd::x11OffersChanged(const QStringList &added, const QStringList &removed) 0121 { 0122 } 0123 0124 bool Dnd::handleClientMessage(xcb_client_message_event_t *event) 0125 { 0126 for (Drag *drag : std::as_const(m_oldDrags)) { 0127 if (drag->handleClientMessage(event)) { 0128 return true; 0129 } 0130 } 0131 if (m_currentDrag && m_currentDrag->handleClientMessage(event)) { 0132 return true; 0133 } 0134 return false; 0135 } 0136 0137 DragEventReply Dnd::dragMoveFilter(Window *target) 0138 { 0139 Q_ASSERT(m_currentDrag); 0140 return m_currentDrag->moveFilter(target); 0141 } 0142 0143 void Dnd::startDrag() 0144 { 0145 auto dragSource = waylandServer()->seat()->dragSource(); 0146 if (qobject_cast<XwlDataSource *>(dragSource)) { 0147 return; 0148 } 0149 0150 // There can only ever be one Wl native drag at the same time. 0151 Q_ASSERT(!m_currentDrag); 0152 0153 // New Wl to X drag, init drag and Wl source. 0154 m_currentDrag = new WlToXDrag(this); 0155 auto source = new WlSource(this); 0156 source->setDataSourceIface(dragSource); 0157 connect(dragSource, &AbstractDataSource::aboutToBeDestroyed, this, [this, source] { 0158 if (source == wlSource()) { 0159 setWlSource(nullptr); 0160 } 0161 }); 0162 setWlSource(source); 0163 ownSelection(true); 0164 } 0165 0166 void Dnd::endDrag() 0167 { 0168 Q_ASSERT(m_currentDrag); 0169 0170 connect(m_currentDrag, &Drag::finish, this, &Dnd::clearOldDrag); 0171 m_oldDrags << m_currentDrag; 0172 0173 m_currentDrag = nullptr; 0174 } 0175 0176 void Dnd::clearOldDrag(Drag *drag) 0177 { 0178 m_oldDrags.removeOne(drag); 0179 delete drag; 0180 } 0181 0182 using DnDAction = DataDeviceManagerInterface::DnDAction; 0183 using DnDActions = DataDeviceManagerInterface::DnDActions; 0184 0185 DnDAction Dnd::atomToClientAction(xcb_atom_t atom) 0186 { 0187 if (atom == atoms->xdnd_action_copy) { 0188 return DnDAction::Copy; 0189 } else if (atom == atoms->xdnd_action_move) { 0190 return DnDAction::Move; 0191 } else if (atom == atoms->xdnd_action_ask) { 0192 // we currently do not support it - need some test client first 0193 return DnDAction::None; 0194 // return DnDAction::Ask; 0195 } 0196 return DnDAction::None; 0197 } 0198 0199 xcb_atom_t Dnd::clientActionToAtom(DnDAction action) 0200 { 0201 if (action == DnDAction::Copy) { 0202 return atoms->xdnd_action_copy; 0203 } else if (action == DnDAction::Move) { 0204 return atoms->xdnd_action_move; 0205 } else if (action == DnDAction::Ask) { 0206 // we currently do not support it - need some test client first 0207 return XCB_ATOM_NONE; 0208 // return atoms->xdnd_action_ask; 0209 } 0210 return XCB_ATOM_NONE; 0211 } 0212 0213 } // namespace Xwl 0214 } // namespace KWin 0215 0216 #include "moc_dnd.cpp"