File indexing completed on 2024-05-12 05:32:34

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"