21#include "wayland/datasource.h"
37using DnDActions = KWin::DataDeviceManagerInterface::DnDActions;
39static QStringList atomToMimeTypes(xcb_atom_t atom)
41 QStringList mimeTypes;
44 mimeTypes << QString::fromLatin1(
"text/plain;charset=utf-8");
46 mimeTypes << QString::fromLatin1(
"text/plain");
48 mimeTypes << QString::fromLatin1(
"text/uri-list") << QString::fromLatin1(
"text/x-uri");
50 mimeTypes << QStringLiteral(
"text/x-moz-url");
52 mimeTypes << QStringLiteral(
"_NETSCAPE_URL");
66 auto it = std::find_if(m_dataRequests.begin(), m_dataRequests.end(), [eventTime](
const QPair<xcb_timestamp_t, bool> &req) {
67 return req.first == eventTime && req.second == false;
69 if (it == m_dataRequests.end()) {
77 m_dataRequests << QPair<xcb_timestamp_t, bool>(m_source->
timestamp(),
false);
86 QTimer::singleShot(2000,
this, [
this] {
90 }
else if (m_dataRequests.size() == 0) {
92 m_visit->sendFinished();
104 if (m_visit && !m_visit->
leave()) {
105 connect(m_visit, &WlVisit::finish, this, &XToWlDrag::checkForFinished);
114 seat->startDrag(&m_selectionSource, seat->focusedPointerSurface(), serial);
125 if (m_visit && m_visit->
target() == target) {
130 if (m_visit->
leave()) {
134 m_oldVisits.removeOne(visit);
137 m_oldVisits << m_visit;
140 const bool hasCurrent = m_visit;
149 seat->setDragTarget(
nullptr,
nullptr);
154 auto *ac =
static_cast<Window *
>(target);
155 m_visit =
new WlVisit(ac,
this, m_dnd);
162 for (
auto *visit : std::as_const(m_oldVisits)) {
163 if (visit->handleClientMessage(event)) {
183void XToWlDrag::setOffers(
const Mimes &offers)
186 if (offers.isEmpty()) {
192 if (m_offers == offers) {
200 QStringList mimeTypes;
201 mimeTypes.reserve(offers.size());
202 for (
const auto &mimePair : offers) {
203 mimeTypes.append(mimePair.first);
209using Mime = QPair<QString, xcb_atom_t>;
211void XToWlDrag::setDragTarget()
217 auto *ac = m_visit->
target();
220 auto dropTarget = seat->dropHandlerForSurface(ac->surface());
222 if (!dropTarget || !ac->surface()) {
225 seat->setDragTarget(dropTarget, ac->surface(), ac->inputTransformation());
228bool XToWlDrag::checkForFinished()
238 if (m_dataRequests.size() == 0 && m_selectionSource.
isAccepted()) {
242 const bool transfersFinished = std::all_of(m_dataRequests.begin(), m_dataRequests.end(),
243 [](QPair<xcb_timestamp_t, bool> req) {
246 if (transfersFinished) {
250 return transfersFinished;
259 xcb_connection_t *xcbConn = kwinApp()->x11Connection();
261 m_window = xcb_generate_id(xcbConn);
264 const uint32_t dndValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE};
265 xcb_create_window(xcbConn,
266 XCB_COPY_FROM_PARENT,
268 kwinApp()->x11RootWindow(),
272 XCB_WINDOW_CLASS_INPUT_OUTPUT,
273 XCB_COPY_FROM_PARENT,
278 xcb_change_property(xcbConn,
279 XCB_PROP_MODE_REPLACE,
285 xcb_map_window(xcbConn, m_window);
295 xcb_connection_t *xcbConn = kwinApp()->x11Connection();
296 xcb_destroy_window(xcbConn, m_window);
309 if (event->window != m_window) {
315 return handleEnter(event);
317 return handlePosition(event);
319 return handleDrop(event);
321 return handleLeave(event);
326static bool hasMimeName(
const Mimes &mimes,
const QString &name)
328 return std::any_of(mimes.begin(), mimes.end(),
329 [name](
const Mime &m) {
330 return m.first == name;
334bool WlVisit::handleEnter(xcb_client_message_event_t *event)
342 xcb_client_message_data_t *data = &
event->data;
343 m_srcWindow = data->data32[0];
344 m_version = data->data32[1] >> 24;
348 if (!(data->data32[1] & 1)) {
350 for (
size_t i = 0; i < 3; i++) {
351 xcb_atom_t mimeAtom = data->data32[2 + i];
352 const auto mimeStrings = atomToMimeTypes(mimeAtom);
353 for (
const auto &mime : mimeStrings) {
354 if (!hasMimeName(offers, mime)) {
355 offers <<
Mime(mime, mimeAtom);
361 getMimesFromWinProperty(offers);
368void WlVisit::getMimesFromWinProperty(
Mimes &offers)
370 xcb_connection_t *xcbConn = kwinApp()->x11Connection();
371 auto cookie = xcb_get_property(xcbConn,
375 XCB_GET_PROPERTY_TYPE_ANY,
378 auto *reply = xcb_get_property_reply(xcbConn, cookie,
nullptr);
379 if (reply ==
nullptr) {
382 if (reply->type != XCB_ATOM_ATOM || reply->value_len == 0) {
388 xcb_atom_t *mimeAtoms =
static_cast<xcb_atom_t *
>(xcb_get_property_value(reply));
389 for (
size_t i = 0; i < reply->value_len; ++i) {
390 const auto mimeStrings = atomToMimeTypes(mimeAtoms[i]);
391 for (
const auto &mime : mimeStrings) {
392 if (!hasMimeName(offers, mime)) {
393 offers <<
Mime(mime, mimeAtoms[i]);
400bool WlVisit::handlePosition(xcb_client_message_event_t *event)
402 xcb_client_message_data_t *data = &
event->data;
403 m_srcWindow = data->data32[0];
407 m_action = DnDAction::None;
408 m_actionAtom = XCB_ATOM_NONE;
413 const xcb_timestamp_t timestamp = data->data32[3];
419 if (action == DnDAction::None) {
421 action = DnDAction::Copy;
425 if (m_action != action) {
427 m_actionAtom = actionAtom;
435bool WlVisit::handleDrop(xcb_client_message_event_t *event)
437 m_dropHandled =
true;
439 xcb_client_message_data_t *data = &
event->data;
440 m_srcWindow = data->data32[0];
441 const xcb_timestamp_t timestamp = data->data32[2];
450void WlVisit::doFinish()
457bool WlVisit::handleLeave(xcb_client_message_event_t *event)
460 xcb_client_message_data_t *data = &
event->data;
461 m_srcWindow = data->data32[0];
466void WlVisit::sendStatus()
469 uint32_t flags = 1 << 1;
470 if (targetAcceptsAction()) {
474 xcb_client_message_data_t data = {};
475 data.data32[0] = m_window;
476 data.data32[1] = flags;
477 data.data32[4] = flags & (1 << 0) ? m_actionAtom : static_cast<uint32_t>(XCB_ATOM_NONE);
483 const bool accepted = m_entered && m_action != DnDAction::None;
484 xcb_client_message_data_t data = {};
485 data.data32[0] = m_window;
486 data.data32[1] = accepted;
487 data.data32[2] = accepted ? m_actionAtom :
static_cast<uint32_t
>(XCB_ATOM_NONE);
491bool WlVisit::targetAcceptsAction()
const
493 if (m_action == DnDAction::None) {
497 return selAction == m_action || selAction == DnDAction::Copy;
500void WlVisit::unmapProxyWindow()
505 xcb_connection_t *xcbConn = kwinApp()->x11Connection();
506 xcb_unmap_window(xcbConn, m_window);
516#include "moc_drag_x.cpp"
Xcb::Atom xdnd_action_copy
quint32 pointerButtonSerial(quint32 button) const
ClientConnection * client() const
SeatInterface * seat() const
SurfaceInterface * surface() const
void updateStackingOrder(bool propagate_new_windows=false)
void removeManualOverlay(xcb_window_t id)
void addManualOverlay(xcb_window_t id)
static uint32_t version()
static DnDAction atomToClientAction(xcb_atom_t atom)
static void sendClientMessage(xcb_window_t target, xcb_atom_t type, xcb_client_message_data_t *data)
void overwriteRequestorWindow(xcb_window_t window)
void transferFinished(xcb_timestamp_t eventTime)
static QString atomName(xcb_atom_t atom)
xcb_timestamp_t timestamp() const
void setTimestamp(xcb_timestamp_t time)
WlVisit(Window *target, XToWlDrag *drag, Dnd *dnd)
void finish(WlVisit *self)
bool handleClientMessage(xcb_client_message_event_t *event)
void offersReceived(const Mimes &offers)
void transferReady(xcb_atom_t target, qint32 fd)
void startTransfer(const QString &mimeName, qint32 fd)
void setOffers(const Mimes &offers)
bool handleClientMessage(xcb_client_message_event_t *event) override
void setDragAndDropAction(DataDeviceManagerInterface::DnDAction action)
DataDeviceManagerInterface::DnDAction selectedDragAndDropAction()
X11Source * x11Source() const
DragEventReply moveFilter(Window *target) override
XToWlDrag(X11Source *source, Dnd *dnd)
void dataRequested(const QString &mimeType, qint32 fd)
bool isAccepted() const override
void setMimeTypes(const QStringList &mimeTypes)
void setSupportedDndActions(DataDeviceManagerInterface::DnDActions dndActions)
DataDeviceManagerInterface::DnDAction selectedDndAction() const override
KWin::DataDeviceManagerInterface::DnDActions DnDActions
QPair< QString, xcb_atom_t > Mime
QList< QPair< QString, xcb_atom_t > > Mimes
WaylandServer * waylandServer()
KWIN_EXPORT Atoms * atoms