File indexing completed on 2025-03-16 11:21:57
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_interface.h" 0019 #include "wayland/datasource_interface.h" 0020 #include "wayland/seat_interface.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(), &KWaylandServer::SeatInterface::dragStarted, this, &Dnd::startDrag); 0077 connect(waylandServer()->seat(), &KWaylandServer::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::x11OffersChanged(const QStringList &added, const QStringList &removed) 0117 { 0118 } 0119 0120 bool Dnd::handleClientMessage(xcb_client_message_event_t *event) 0121 { 0122 for (Drag *drag : std::as_const(m_oldDrags)) { 0123 if (drag->handleClientMessage(event)) { 0124 return true; 0125 } 0126 } 0127 if (m_currentDrag && m_currentDrag->handleClientMessage(event)) { 0128 return true; 0129 } 0130 return false; 0131 } 0132 0133 DragEventReply Dnd::dragMoveFilter(Window *target, const QPoint &pos) 0134 { 0135 Q_ASSERT(m_currentDrag); 0136 return m_currentDrag->moveFilter(target, pos); 0137 } 0138 0139 void Dnd::startDrag() 0140 { 0141 auto dragSource = waylandServer()->seat()->dragSource(); 0142 if (qobject_cast<XwlDataSource *>(dragSource)) { 0143 return; 0144 } 0145 0146 // There can only ever be one Wl native drag at the same time. 0147 Q_ASSERT(!m_currentDrag); 0148 0149 // New Wl to X drag, init drag and Wl source. 0150 m_currentDrag = new WlToXDrag(this); 0151 auto source = new WlSource(this); 0152 source->setDataSourceIface(dragSource); 0153 connect(dragSource, &KWaylandServer::AbstractDataSource::aboutToBeDestroyed, this, [this, source] { 0154 if (source == wlSource()) { 0155 setWlSource(nullptr); 0156 } 0157 }); 0158 setWlSource(source); 0159 ownSelection(true); 0160 } 0161 0162 void Dnd::endDrag() 0163 { 0164 Q_ASSERT(m_currentDrag); 0165 0166 connect(m_currentDrag, &Drag::finish, this, &Dnd::clearOldDrag); 0167 m_oldDrags << m_currentDrag; 0168 0169 m_currentDrag = nullptr; 0170 } 0171 0172 void Dnd::clearOldDrag(Drag *drag) 0173 { 0174 m_oldDrags.removeOne(drag); 0175 delete drag; 0176 } 0177 0178 using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction; 0179 using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions; 0180 0181 DnDAction Dnd::atomToClientAction(xcb_atom_t atom) 0182 { 0183 if (atom == atoms->xdnd_action_copy) { 0184 return DnDAction::Copy; 0185 } else if (atom == atoms->xdnd_action_move) { 0186 return DnDAction::Move; 0187 } else if (atom == atoms->xdnd_action_ask) { 0188 // we currently do not support it - need some test client first 0189 return DnDAction::None; 0190 // return DnDAction::Ask; 0191 } 0192 return DnDAction::None; 0193 } 0194 0195 xcb_atom_t Dnd::clientActionToAtom(DnDAction action) 0196 { 0197 if (action == DnDAction::Copy) { 0198 return atoms->xdnd_action_copy; 0199 } else if (action == DnDAction::Move) { 0200 return atoms->xdnd_action_move; 0201 } else if (action == DnDAction::Ask) { 0202 // we currently do not support it - need some test client first 0203 return XCB_ATOM_NONE; 0204 // return atoms->xdnd_action_ask; 0205 } 0206 return XCB_ATOM_NONE; 0207 } 0208 0209 } // namespace Xwl 0210 } // namespace KWin