2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2018-08-22 12:56:48 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
2021-09-03 11:48:09 +00:00
|
|
|
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
|
2018-08-22 12:56:48 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2018-08-22 12:56:48 +00:00
|
|
|
#include "drag_x.h"
|
|
|
|
|
|
|
|
#include "databridge.h"
|
|
|
|
#include "dnd.h"
|
|
|
|
#include "selection_source.h"
|
|
|
|
#include "xwayland.h"
|
2021-09-03 11:48:09 +00:00
|
|
|
#include "datasource.h"
|
2018-08-22 12:56:48 +00:00
|
|
|
|
|
|
|
#include "abstract_client.h"
|
|
|
|
#include "atoms.h"
|
|
|
|
#include "wayland_server.h"
|
|
|
|
#include "workspace.h"
|
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/datasource_interface.h>
|
|
|
|
#include <KWaylandServer/seat_interface.h>
|
|
|
|
#include <KWaylandServer/surface_interface.h>
|
2021-09-03 11:48:09 +00:00
|
|
|
#include <KWaylandServer/datadevice_interface.h>
|
2018-08-22 12:56:48 +00:00
|
|
|
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QTimer>
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
namespace Xwl
|
|
|
|
{
|
2018-08-22 12:56:48 +00:00
|
|
|
|
2021-09-03 11:48:09 +00:00
|
|
|
using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction;
|
|
|
|
using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions;
|
2021-06-03 09:04:26 +00:00
|
|
|
|
2018-08-24 21:05:35 +00:00
|
|
|
static QStringList atomToMimeTypes(xcb_atom_t atom)
|
|
|
|
{
|
|
|
|
QStringList mimeTypes;
|
|
|
|
|
|
|
|
if (atom == atoms->utf8_string) {
|
|
|
|
mimeTypes << QString::fromLatin1("text/plain;charset=utf-8");
|
|
|
|
} else if (atom == atoms->text) {
|
|
|
|
mimeTypes << QString::fromLatin1("text/plain");
|
|
|
|
} else if (atom == atoms->uri_list || atom == atoms->netscape_url || atom == atoms->moz_url) {
|
|
|
|
// We identify netscape and moz format as less detailed formats text/uri-list,
|
|
|
|
// text/x-uri and accept the information loss.
|
|
|
|
mimeTypes << QString::fromLatin1("text/uri-list") << QString::fromLatin1("text/x-uri");
|
|
|
|
} else {
|
|
|
|
mimeTypes << Selection::atomName(atom);
|
|
|
|
}
|
|
|
|
return mimeTypes;
|
|
|
|
}
|
|
|
|
|
2018-08-22 12:56:48 +00:00
|
|
|
XToWlDrag::XToWlDrag(X11Source *source)
|
2019-07-02 19:56:03 +00:00
|
|
|
: m_source(source)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
|
|
|
connect(DataBridge::self()->dnd(), &Dnd::transferFinished, this, [this](xcb_timestamp_t eventTime) {
|
|
|
|
// we use this mechanism, because the finished call is not
|
|
|
|
// reliable done by Wayland clients
|
2020-03-15 19:59:29 +00:00
|
|
|
auto it = std::find_if(m_dataRequests.begin(), m_dataRequests.end(), [eventTime](const QPair<xcb_timestamp_t, bool> &req) {
|
2020-04-06 17:10:03 +00:00
|
|
|
return req.first == eventTime && req.second == false;
|
2018-08-22 12:56:48 +00:00
|
|
|
});
|
|
|
|
if (it == m_dataRequests.end()) {
|
|
|
|
// transfer finished for a different drag
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
(*it).second = true;
|
|
|
|
checkForFinished();
|
|
|
|
});
|
|
|
|
connect(source, &X11Source::transferReady, this, [this](xcb_atom_t target, qint32 fd) {
|
|
|
|
Q_UNUSED(target);
|
|
|
|
Q_UNUSED(fd);
|
2019-07-02 19:56:03 +00:00
|
|
|
m_dataRequests << QPair<xcb_timestamp_t, bool>(m_source->timestamp(), false);
|
2018-08-22 12:56:48 +00:00
|
|
|
});
|
2021-09-03 11:48:09 +00:00
|
|
|
connect(&m_selectionSource, &XwlDataSource::dropped, this, [this] {
|
2018-08-22 12:56:48 +00:00
|
|
|
m_performed = true;
|
|
|
|
if (m_visit) {
|
|
|
|
connect(m_visit, &WlVisit::finish, this, [this](WlVisit *visit) {
|
|
|
|
Q_UNUSED(visit);
|
|
|
|
checkForFinished();
|
|
|
|
});
|
|
|
|
|
|
|
|
QTimer::singleShot(2000, this, [this]{
|
|
|
|
if (!m_visit->entered() || !m_visit->dropHandled()) {
|
|
|
|
// X client timed out
|
|
|
|
Q_EMIT finish(this);
|
|
|
|
} else if (m_dataRequests.size() == 0) {
|
|
|
|
// Wl client timed out
|
|
|
|
m_visit->sendFinished();
|
|
|
|
Q_EMIT finish(this);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-09-03 11:48:09 +00:00
|
|
|
// Dave do we need this async finish check anymore?
|
2018-08-22 12:56:48 +00:00
|
|
|
checkForFinished();
|
|
|
|
});
|
2021-09-03 11:48:09 +00:00
|
|
|
connect(&m_selectionSource, &XwlDataSource::finished, this, [this] {
|
2018-08-22 12:56:48 +00:00
|
|
|
checkForFinished();
|
|
|
|
});
|
2022-02-03 13:26:05 +00:00
|
|
|
connect(&m_selectionSource, &XwlDataSource::cancelled, this, [this] {
|
|
|
|
if (m_visit && !m_visit->leave()) {
|
|
|
|
connect(m_visit, &WlVisit::finish, this, &XToWlDrag::checkForFinished);
|
|
|
|
}
|
|
|
|
checkForFinished();
|
|
|
|
});
|
2021-09-03 11:48:09 +00:00
|
|
|
connect(&m_selectionSource, &XwlDataSource::dataRequested, source, &X11Source::startTransfer);
|
2018-08-22 12:56:48 +00:00
|
|
|
|
2021-09-03 11:48:09 +00:00
|
|
|
auto *seat = waylandServer()->seat();
|
|
|
|
int serial = waylandServer()->seat()->pointerButtonSerial(Qt::LeftButton);
|
|
|
|
// we know we are the focussed surface as dnd checks
|
|
|
|
seat->startDrag(&m_selectionSource, seat->focusedPointerSurface(), serial);
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
XToWlDrag::~XToWlDrag()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
DragEventReply XToWlDrag::moveFilter(Toplevel *target, const QPoint &pos)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(pos);
|
|
|
|
|
|
|
|
auto *seat = waylandServer()->seat();
|
|
|
|
|
|
|
|
if (m_visit && m_visit->target() == target) {
|
|
|
|
// still same Wl target, wait for X events
|
|
|
|
return DragEventReply::Ignore;
|
|
|
|
}
|
|
|
|
if (m_visit) {
|
|
|
|
if (m_visit->leave()) {
|
|
|
|
delete m_visit;
|
|
|
|
} else {
|
|
|
|
connect(m_visit, &WlVisit::finish, this, [this](WlVisit *visit) {
|
|
|
|
m_oldVisits.removeOne(visit);
|
|
|
|
delete visit;
|
|
|
|
});
|
|
|
|
m_oldVisits << m_visit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const bool hasCurrent = m_visit;
|
|
|
|
m_visit = nullptr;
|
|
|
|
|
|
|
|
if (!target || !target->surface() ||
|
|
|
|
target->surface()->client() == waylandServer()->xWaylandConnection()) {
|
|
|
|
// currently there is no target or target is an Xwayland window
|
|
|
|
// handled here and by X directly
|
|
|
|
if (hasCurrent) {
|
|
|
|
// last received enter event is now void,
|
|
|
|
// wait for the next one
|
2021-09-03 11:48:09 +00:00
|
|
|
seat->setDragTarget(nullptr, nullptr);
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
return DragEventReply::Ignore;
|
|
|
|
}
|
|
|
|
// new Wl native target
|
|
|
|
auto *ac = static_cast<AbstractClient*>(target);
|
|
|
|
m_visit = new WlVisit(ac, this);
|
|
|
|
connect(m_visit, &WlVisit::offersReceived, this, &XToWlDrag::setOffers);
|
|
|
|
return DragEventReply::Ignore;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XToWlDrag::handleClientMessage(xcb_client_message_event_t *event)
|
|
|
|
{
|
2021-05-13 16:41:39 +00:00
|
|
|
for (auto *visit : qAsConst(m_oldVisits)) {
|
2018-08-22 12:56:48 +00:00
|
|
|
if (visit->handleClientMessage(event)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_visit && m_visit->handleClientMessage(event)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XToWlDrag::setDragAndDropAction(DnDAction action)
|
|
|
|
{
|
2021-09-03 11:48:09 +00:00
|
|
|
m_selectionSource.setSupportedDndActions(action);
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DnDAction XToWlDrag::selectedDragAndDropAction()
|
|
|
|
{
|
2021-09-03 11:48:09 +00:00
|
|
|
return m_selectionSource.selectedDragAndDropAction();
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void XToWlDrag::setOffers(const Mimes &offers)
|
|
|
|
{
|
2019-07-02 19:56:03 +00:00
|
|
|
m_source->setOffers(offers);
|
2018-08-22 12:56:48 +00:00
|
|
|
if (offers.isEmpty()) {
|
|
|
|
// There are no offers, so just directly set the drag target,
|
|
|
|
// no transfer possible anyways.
|
|
|
|
setDragTarget();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_offers == offers) {
|
|
|
|
// offers had been set already by a previous visit
|
|
|
|
// Wl side is already configured
|
|
|
|
setDragTarget();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-03 11:48:09 +00:00
|
|
|
m_offers = offers;
|
|
|
|
QStringList mimeTypes;
|
|
|
|
mimeTypes.reserve(offers.size());
|
2020-08-26 17:24:02 +00:00
|
|
|
for (const auto &mimePair : offers) {
|
2021-09-03 11:48:09 +00:00
|
|
|
mimeTypes.append(mimePair.first);
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
2021-09-03 11:48:09 +00:00
|
|
|
m_selectionSource.setMimeTypes(mimeTypes);
|
|
|
|
setDragTarget();
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
using Mime = QPair<QString, xcb_atom_t>;
|
|
|
|
|
|
|
|
void XToWlDrag::setDragTarget()
|
|
|
|
{
|
|
|
|
auto *ac = m_visit->target();
|
2021-09-03 11:47:57 +00:00
|
|
|
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
auto dropTarget = seat->dropHandlerForSurface(ac->surface());
|
2021-09-03 11:48:09 +00:00
|
|
|
|
2021-09-03 11:47:57 +00:00
|
|
|
if (!dropTarget || !ac->surface()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
seat->setDragTarget(dropTarget, ac->surface(), ac->inputTransformation());
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool XToWlDrag::checkForFinished()
|
|
|
|
{
|
|
|
|
if (!m_visit) {
|
|
|
|
// not dropped above Wl native target
|
|
|
|
Q_EMIT finish(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!m_visit->finished()) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-03 13:26:05 +00:00
|
|
|
if (m_dataRequests.size() == 0 && m_selectionSource.isAccepted()) {
|
2018-08-22 12:56:48 +00:00
|
|
|
// need to wait for first data request
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const bool transfersFinished = std::all_of(m_dataRequests.begin(), m_dataRequests.end(),
|
|
|
|
[](QPair<xcb_timestamp_t, bool> req) { return req.second; });
|
|
|
|
if (transfersFinished) {
|
|
|
|
m_visit->sendFinished();
|
|
|
|
Q_EMIT finish(this);
|
|
|
|
}
|
|
|
|
return transfersFinished;
|
|
|
|
}
|
|
|
|
|
|
|
|
WlVisit::WlVisit(AbstractClient *target, XToWlDrag *drag)
|
|
|
|
: QObject(drag),
|
|
|
|
m_target(target),
|
|
|
|
m_drag(drag)
|
|
|
|
{
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_connection_t *xcbConn = kwinApp()->x11Connection();
|
2018-08-22 12:56:48 +00:00
|
|
|
|
|
|
|
m_window = xcb_generate_id(xcbConn);
|
|
|
|
DataBridge::self()->dnd()->overwriteRequestorWindow(m_window);
|
|
|
|
|
|
|
|
const uint32_t dndValues[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
|
|
|
|
XCB_EVENT_MASK_PROPERTY_CHANGE };
|
|
|
|
xcb_create_window(xcbConn,
|
|
|
|
XCB_COPY_FROM_PARENT,
|
|
|
|
m_window,
|
|
|
|
kwinApp()->x11RootWindow(),
|
|
|
|
0, 0,
|
|
|
|
8192, 8192, // TODO: get current screen size and connect to changes
|
|
|
|
0,
|
|
|
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
2020-07-28 07:58:35 +00:00
|
|
|
XCB_COPY_FROM_PARENT,
|
2018-08-22 12:56:48 +00:00
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
dndValues);
|
|
|
|
|
|
|
|
uint32_t version = Dnd::version();
|
|
|
|
xcb_change_property(xcbConn,
|
|
|
|
XCB_PROP_MODE_REPLACE,
|
|
|
|
m_window,
|
|
|
|
atoms->xdnd_aware,
|
|
|
|
XCB_ATOM_ATOM,
|
|
|
|
32, 1, &version);
|
|
|
|
|
|
|
|
xcb_map_window(xcbConn, m_window);
|
2018-08-24 21:10:48 +00:00
|
|
|
workspace()->addManualOverlay(m_window);
|
|
|
|
workspace()->updateStackingOrder(true);
|
2018-08-22 12:56:48 +00:00
|
|
|
|
|
|
|
xcb_flush(xcbConn);
|
|
|
|
m_mapped = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
WlVisit::~WlVisit()
|
|
|
|
{
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_connection_t *xcbConn = kwinApp()->x11Connection();
|
2018-08-22 12:56:48 +00:00
|
|
|
xcb_destroy_window(xcbConn, m_window);
|
|
|
|
xcb_flush(xcbConn);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WlVisit::leave()
|
|
|
|
{
|
|
|
|
DataBridge::self()->dnd()->overwriteRequestorWindow(XCB_WINDOW_NONE);
|
|
|
|
unmapProxyWindow();
|
|
|
|
return m_finished;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WlVisit::handleClientMessage(xcb_client_message_event_t *event)
|
|
|
|
{
|
|
|
|
if (event->window != m_window) {
|
|
|
|
// different window
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->type == atoms->xdnd_enter) {
|
|
|
|
return handleEnter(event);
|
|
|
|
} else if (event->type == atoms->xdnd_position) {
|
|
|
|
return handlePosition(event);
|
|
|
|
} else if (event->type == atoms->xdnd_drop) {
|
|
|
|
return handleDrop(event);
|
|
|
|
} else if (event->type == atoms->xdnd_leave) {
|
|
|
|
return handleLeave(event);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasMimeName(const Mimes &mimes, const QString &name)
|
|
|
|
{
|
|
|
|
return std::any_of(mimes.begin(), mimes.end(),
|
|
|
|
[name](const Mime &m) { return m.first == name; });
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
bool WlVisit::handleEnter(xcb_client_message_event_t *event)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
|
|
|
if (m_entered) {
|
|
|
|
// a drag already entered
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
m_entered = true;
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_client_message_data_t *data = &event->data;
|
2018-08-22 12:56:48 +00:00
|
|
|
m_srcWindow = data->data32[0];
|
|
|
|
m_version = data->data32[1] >> 24;
|
|
|
|
|
|
|
|
// get types
|
|
|
|
Mimes offers;
|
|
|
|
if (!(data->data32[1] & 1)) {
|
|
|
|
// message has only max 3 types (which are directly in data)
|
|
|
|
for (size_t i = 0; i < 3; i++) {
|
|
|
|
xcb_atom_t mimeAtom = data->data32[2 + i];
|
2018-08-24 21:05:35 +00:00
|
|
|
const auto mimeStrings = atomToMimeTypes(mimeAtom);
|
2020-08-26 17:24:02 +00:00
|
|
|
for (const auto &mime : mimeStrings ) {
|
2018-08-22 12:56:48 +00:00
|
|
|
if (!hasMimeName(offers, mime)) {
|
|
|
|
offers << Mime(mime, mimeAtom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// more than 3 types -> in window property
|
|
|
|
getMimesFromWinProperty(offers);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_EMIT offersReceived(offers);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WlVisit::getMimesFromWinProperty(Mimes &offers)
|
|
|
|
{
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_connection_t *xcbConn = kwinApp()->x11Connection();
|
2018-08-22 12:56:48 +00:00
|
|
|
auto cookie = xcb_get_property(xcbConn,
|
|
|
|
0,
|
|
|
|
m_srcWindow,
|
|
|
|
atoms->xdnd_type_list,
|
|
|
|
XCB_GET_PROPERTY_TYPE_ANY,
|
|
|
|
0, 0x1fffffff);
|
|
|
|
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr);
|
|
|
|
if (reply == nullptr) {
|
2018-08-22 12:56:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (reply->type != XCB_ATOM_ATOM || reply->value_len == 0) {
|
|
|
|
// invalid reply value
|
|
|
|
free(reply);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_atom_t *mimeAtoms = static_cast<xcb_atom_t *>(xcb_get_property_value(reply));
|
2018-08-22 12:56:48 +00:00
|
|
|
for (size_t i = 0; i < reply->value_len; ++i) {
|
2018-08-24 21:05:35 +00:00
|
|
|
const auto mimeStrings = atomToMimeTypes(mimeAtoms[i]);
|
2020-08-26 17:24:02 +00:00
|
|
|
for (const auto &mime : mimeStrings) {
|
2018-08-22 12:56:48 +00:00
|
|
|
if (!hasMimeName(offers, mime)) {
|
|
|
|
offers << Mime(mime, mimeAtoms[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
bool WlVisit::handlePosition(xcb_client_message_event_t *event)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_client_message_data_t *data = &event->data;
|
2018-08-22 12:56:48 +00:00
|
|
|
m_srcWindow = data->data32[0];
|
|
|
|
|
|
|
|
if (!m_target) {
|
|
|
|
// not over Wl window at the moment
|
|
|
|
m_action = DnDAction::None;
|
|
|
|
m_actionAtom = XCB_ATOM_NONE;
|
|
|
|
sendStatus();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const uint32_t pos = data->data32[2];
|
|
|
|
Q_UNUSED(pos);
|
|
|
|
|
|
|
|
const xcb_timestamp_t timestamp = data->data32[3];
|
|
|
|
m_drag->x11Source()->setTimestamp(timestamp);
|
|
|
|
|
|
|
|
xcb_atom_t actionAtom = m_version > 1 ? data->data32[4] :
|
|
|
|
atoms->xdnd_action_copy;
|
2021-09-03 11:48:09 +00:00
|
|
|
auto action = Dnd::atomToClientAction(actionAtom);
|
2018-08-22 12:56:48 +00:00
|
|
|
|
|
|
|
if (action == DnDAction::None) {
|
|
|
|
// copy action is always possible in XDND
|
|
|
|
action = DnDAction::Copy;
|
|
|
|
actionAtom = atoms->xdnd_action_copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_action != action) {
|
|
|
|
m_action = action;
|
|
|
|
m_actionAtom = actionAtom;
|
|
|
|
m_drag->setDragAndDropAction(m_action);
|
|
|
|
}
|
|
|
|
|
|
|
|
sendStatus();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
bool WlVisit::handleDrop(xcb_client_message_event_t *event)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
|
|
|
m_dropHandled = true;
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_client_message_data_t *data = &event->data;
|
2018-08-22 12:56:48 +00:00
|
|
|
m_srcWindow = data->data32[0];
|
|
|
|
const xcb_timestamp_t timestamp = data->data32[2];
|
|
|
|
m_drag->x11Source()->setTimestamp(timestamp);
|
|
|
|
|
|
|
|
// we do nothing more here, the drop is being processed
|
|
|
|
// through the X11Source object
|
|
|
|
doFinish();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WlVisit::doFinish()
|
|
|
|
{
|
|
|
|
m_finished = true;
|
|
|
|
unmapProxyWindow();
|
|
|
|
Q_EMIT finish(this);
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
bool WlVisit::handleLeave(xcb_client_message_event_t *event)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
|
|
|
m_entered = false;
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_client_message_data_t *data = &event->data;
|
2018-08-22 12:56:48 +00:00
|
|
|
m_srcWindow = data->data32[0];
|
|
|
|
doFinish();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WlVisit::sendStatus()
|
|
|
|
{
|
|
|
|
// receive position events
|
|
|
|
uint32_t flags = 1 << 1;
|
|
|
|
if (targetAcceptsAction()) {
|
|
|
|
// accept the drop
|
|
|
|
flags |= (1 << 0);
|
|
|
|
}
|
2020-08-26 17:24:02 +00:00
|
|
|
xcb_client_message_data_t data = {};
|
2018-08-22 12:56:48 +00:00
|
|
|
data.data32[0] = m_window;
|
|
|
|
data.data32[1] = flags;
|
|
|
|
data.data32[4] = flags & (1 << 0) ? m_actionAtom : static_cast<uint32_t>(XCB_ATOM_NONE);
|
|
|
|
Drag::sendClientMessage(m_srcWindow, atoms->xdnd_status, &data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WlVisit::sendFinished()
|
|
|
|
{
|
|
|
|
const bool accepted = m_entered && m_action != DnDAction::None;
|
2020-08-26 17:24:02 +00:00
|
|
|
xcb_client_message_data_t data = {};
|
2018-08-22 12:56:48 +00:00
|
|
|
data.data32[0] = m_window;
|
|
|
|
data.data32[1] = accepted;
|
|
|
|
data.data32[2] = accepted ? m_actionAtom : static_cast<uint32_t>(XCB_ATOM_NONE);
|
|
|
|
Drag::sendClientMessage(m_srcWindow, atoms->xdnd_finished, &data);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WlVisit::targetAcceptsAction() const
|
|
|
|
{
|
|
|
|
if (m_action == DnDAction::None) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const auto selAction = m_drag->selectedDragAndDropAction();
|
|
|
|
return selAction == m_action || selAction == DnDAction::Copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WlVisit::unmapProxyWindow()
|
|
|
|
{
|
|
|
|
if (!m_mapped) {
|
|
|
|
return;
|
|
|
|
}
|
2019-07-02 19:56:03 +00:00
|
|
|
xcb_connection_t *xcbConn = kwinApp()->x11Connection();
|
2018-08-22 12:56:48 +00:00
|
|
|
xcb_unmap_window(xcbConn, m_window);
|
2018-08-24 21:10:48 +00:00
|
|
|
workspace()->removeManualOverlay(m_window);
|
|
|
|
workspace()->updateStackingOrder(true);
|
2018-08-22 12:56:48 +00:00
|
|
|
xcb_flush(xcbConn);
|
|
|
|
m_mapped = false;
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
} // namespace Xwl
|
|
|
|
} // namespace KWin
|