39 bool isDragging()
const noexcept
45 void handleExternalSelectionClear()
48 externalResetDragAndDrop();
51 void handleExternalSelectionRequest (
const XEvent& evt)
53 auto targetType = evt.xselectionrequest.target;
56 s.xselection.type = SelectionNotify;
57 s.xselection.requestor = evt.xselectionrequest.requestor;
58 s.xselection.selection = evt.xselectionrequest.selection;
59 s.xselection.target = targetType;
60 s.xselection.property = None;
61 s.xselection.time = evt.xselectionrequest.time;
63 auto* display = getDisplay();
65 if (allowedTypes.
contains (targetType))
67 s.xselection.property = evt.xselectionrequest.property;
69 X11Symbols::getInstance()->xChangeProperty (display, evt.xselectionrequest.requestor, evt.xselectionrequest.property,
70 targetType, 8, PropModeReplace,
71 reinterpret_cast<const unsigned char*
> (textOrFiles.
toRawUTF8()),
75 X11Symbols::getInstance()->xSendEvent (display, evt.xselectionrequest.requestor, True, 0, &s);
78 void handleExternalDragAndDropStatus (
const XClientMessageEvent& clientMsg)
82 expectingStatus =
false;
86 const auto& atoms = getAtoms();
88 if ((clientMsg.data.l[1] & 1) != 0
89 && ((Atom) clientMsg.data.l[4] == atoms.XdndActionCopy
90 || (Atom) clientMsg.data.l[4] == atoms.XdndActionPrivate))
92 if ((clientMsg.data.l[1] & 2) == 0)
93 silentRect.
setBounds ((
int) clientMsg.data.l[2] >> 16, (
int) clientMsg.data.l[2] & 0xffff,
94 (
int) clientMsg.data.l[3] >> 16, (
int) clientMsg.data.l[3] & 0xffff);
101 void handleExternalDragButtonReleaseEvent()
104 X11Symbols::getInstance()->xUngrabPointer (getDisplay(), CurrentTime);
108 sendExternalDragAndDropDrop();
112 sendExternalDragAndDropLeave();
113 externalResetDragAndDrop();
117 void handleExternalDragMotionNotify()
119 auto* display = getDisplay();
121 auto newTargetWindow = externalFindDragTargetWindow (X11Symbols::getInstance()
122 ->xRootWindow (display,
123 X11Symbols::getInstance()->xDefaultScreen (display)));
125 if (targetWindow != newTargetWindow)
127 if (targetWindow != None)
128 sendExternalDragAndDropLeave();
133 if (newTargetWindow == None)
136 xdndVersion = getDnDVersionForWindow (newTargetWindow);
138 if (xdndVersion == -1)
141 targetWindow = newTargetWindow;
142 sendExternalDragAndDropEnter();
145 if (! expectingStatus)
146 sendExternalDragAndDropPosition();
149 void handleDragAndDropPosition (
const XClientMessageEvent& clientMsg,
ComponentPeer* peer)
151 if (dragAndDropSourceWindow == 0)
154 dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
161 (
int) clientMsg.data.l[2] & 0xffff));
162 const auto dropPos = detail::ScalingHelpers::screenPosToLocalPos (peer->
getComponent(), logicalPos.toFloat()).roundToInt();
164 const auto& atoms = getAtoms();
166 auto targetAction = atoms.XdndActionCopy;
170 if ((Atom) clientMsg.data.l[4] == atoms.allowedActions[i])
172 targetAction = atoms.allowedActions[i];
177 sendDragAndDropStatus (
true, targetAction);
179 if (dragInfo.position != dropPos)
181 dragInfo.position = dropPos;
183 if (dragInfo.isEmpty())
186 if (! dragInfo.isEmpty())
187 peer->handleDragMove (dragInfo);
191 void handleDragAndDropDrop (
const XClientMessageEvent& clientMsg,
ComponentPeer* peer)
193 if (dragInfo.isEmpty())
196 finishAfterDropDataReceived =
true;
201 handleDragAndDropDataReceived();
205 void handleDragAndDropEnter (
const XClientMessageEvent& clientMsg,
ComponentPeer* peer)
208 srcMimeTypeAtomList.
clear();
210 dragAndDropCurrentMimeType = 0;
211 auto dndCurrentVersion = (
static_cast<unsigned long> (clientMsg.data.l[1]) & 0xff000000) >> 24;
213 if (dndCurrentVersion < 3 || dndCurrentVersion > XWindowSystemUtilities::Atoms::DndVersion)
215 dragAndDropSourceWindow = 0;
219 const auto& atoms = getAtoms();
221 dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
223 if ((clientMsg.data.l[1] & 1) != 0)
228 dragAndDropSourceWindow,
235 if (prop.success && prop.actualType == XA_ATOM && prop.actualFormat == 32 && prop.numItems != 0)
237 auto* types = prop.data;
239 for (
unsigned long i = 0; i < prop.numItems; ++i)
242 memcpy (&type, types,
sizeof (
unsigned long));
245 srcMimeTypeAtomList.
add (type);
247 types +=
sizeof (
unsigned long);
252 if (srcMimeTypeAtomList.
isEmpty())
254 for (
int i = 2; i < 5; ++i)
255 if (clientMsg.data.l[i] != None)
256 srcMimeTypeAtomList.
add ((
unsigned long) clientMsg.data.l[i]);
258 if (srcMimeTypeAtomList.
isEmpty())
260 dragAndDropSourceWindow = 0;
265 for (
int i = 0; i < srcMimeTypeAtomList.
size() && dragAndDropCurrentMimeType == 0; ++i)
267 if (srcMimeTypeAtomList[i] == atoms.allowedMimeTypes[j])
268 dragAndDropCurrentMimeType = atoms.allowedMimeTypes[j];
270 handleDragAndDropPosition (clientMsg, peer);
273 void handleDragAndDropExit()
275 if (
auto* peer = getPeerFor (windowH))
276 peer->handleDragExit (dragInfo);
281 void handleDragAndDropSelection (
const XEvent& evt)
285 if (evt.xselection.property != None)
296 evt.xselection.property,
297 (
long) (dropData.
getSize() / 4),
305 dropData.
append (prop.data, (
size_t) (prop.actualFormat / 8) * prop.numItems);
307 if (prop.bytesLeft <= 0)
314 if (XWindowSystemUtilities::Atoms::isMimeTypeFile (getDisplay(), dragAndDropCurrentMimeType))
316 for (
const auto& line : lines)
318 const auto escaped = line.replace (
"+",
"%2B").replace (
"file://",
String(),
true);
322 dragInfo.files.
trim();
330 if (finishAfterDropDataReceived)
331 handleDragAndDropDataReceived();
335 void externalResetDragAndDrop()
340 X11Symbols::getInstance()->xUngrabPointer (getDisplay(), CurrentTime);
343 NullCheckedInvocation::invoke (completionCallback);
348 bool externalDragInit (::Window window,
bool text,
const String& str,
std::function<
void()>&& cb)
353 targetWindow = windowH;
354 completionCallback = std::move (cb);
356 auto* display = getDisplay();
358 allowedTypes.
add (XWindowSystemUtilities::Atoms::getCreating (display, isText ?
"text/plain" :
"text/uri-list"));
360 auto pointerGrabMask = (
unsigned int) (Button1MotionMask | ButtonReleaseMask);
364 if (X11Symbols::getInstance()->xGrabPointer (display, windowH, True, pointerGrabMask,
365 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess)
367 const auto& atoms = getAtoms();
370 X11Symbols::getInstance()->xChangeActivePointerGrab (display, pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime);
372 X11Symbols::getInstance()->xSetSelectionOwner (display, atoms.XdndSelection, windowH, CurrentTime);
375 X11Symbols::getInstance()->xChangeProperty (display, windowH, atoms.XdndTypeList, XA_ATOM, 32, PropModeReplace,
379 xdndVersion = getDnDVersionForWindow (targetWindow);
381 sendExternalDragAndDropEnter();
382 handleExternalDragMotionNotify();
393 ::Display* getDisplay()
const noexcept {
return XWindowSystem::getInstance()->getDisplay(); }
396 void sendDragAndDropMessage (XClientMessageEvent& msg)
398 auto* display = getDisplay();
400 msg.type = ClientMessage;
401 msg.display = display;
402 msg.window = dragAndDropSourceWindow;
404 msg.data.l[0] = (
long) windowH;
407 X11Symbols::getInstance()->xSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg);
410 bool sendExternalDragAndDropMessage (XClientMessageEvent& msg)
412 auto* display = getDisplay();
414 msg.type = ClientMessage;
415 msg.display = display;
416 msg.window = targetWindow;
418 msg.data.l[0] = (
long) windowH;
421 return X11Symbols::getInstance()->xSendEvent (display, targetWindow, False, 0, (XEvent*) &msg) != 0;
424 void sendExternalDragAndDropDrop()
426 XClientMessageEvent msg;
429 msg.message_type = getAtoms().XdndDrop;
430 msg.data.l[2] = CurrentTime;
432 sendExternalDragAndDropMessage (msg);
435 void sendExternalDragAndDropEnter()
437 XClientMessageEvent msg;
440 msg.message_type = getAtoms().XdndEnter;
441 msg.data.l[1] = (xdndVersion << 24);
443 for (
int i = 0; i < 3; ++i)
444 msg.data.l[i + 2] = (
long) allowedTypes[i];
446 sendExternalDragAndDropMessage (msg);
449 void sendExternalDragAndDropPosition()
451 XClientMessageEvent msg;
454 const auto& atoms = getAtoms();
456 msg.message_type = atoms.XdndPosition;
466 msg.data.l[2] = (mousePos.x << 16) | mousePos.y;
467 msg.data.l[3] = CurrentTime;
468 msg.data.l[4] = (
long) atoms.XdndActionCopy;
470 expectingStatus = sendExternalDragAndDropMessage (msg);
473 void sendDragAndDropStatus (
bool acceptDrop, Atom dropAction)
475 XClientMessageEvent msg;
478 msg.message_type = getAtoms().XdndStatus;
479 msg.data.l[1] = (acceptDrop ? 1 : 0) | 2;
480 msg.data.l[4] = (
long) dropAction;
482 sendDragAndDropMessage (msg);
485 void sendExternalDragAndDropLeave()
487 XClientMessageEvent msg;
490 msg.message_type = getAtoms().XdndLeave;
491 sendExternalDragAndDropMessage (msg);
494 void sendDragAndDropFinish()
496 XClientMessageEvent msg;
499 msg.message_type = getAtoms().XdndFinished;
500 sendDragAndDropMessage (msg);
503 void updateDraggedFileList (
const XClientMessageEvent& clientMsg, ::Window requestor)
507 if (dragAndDropSourceWindow != None && dragAndDropCurrentMimeType != None)
509 auto* display = getDisplay();
512 X11Symbols::getInstance()->xConvertSelection (display, getAtoms().XdndSelection, dragAndDropCurrentMimeType,
513 XWindowSystemUtilities::Atoms::getCreating (display,
"JXSelectionWindowProperty"),
514 requestor, (::Time) clientMsg.data.l[2]);
518 bool isWindowDnDAware (::Window w)
const
520 int numProperties = 0;
521 auto* properties = X11Symbols::getInstance()->xListProperties (getDisplay(), w, &numProperties);
523 bool dndAwarePropFound =
false;
525 for (
int i = 0; i < numProperties; ++i)
526 if (properties[i] == getAtoms().XdndAware)
527 dndAwarePropFound =
true;
529 if (properties !=
nullptr)
530 X11Symbols::getInstance()->xFree (properties);
532 return dndAwarePropFound;
535 int getDnDVersionForWindow (::Window target)
539 getAtoms().XdndAware,
545 if (prop.success && prop.data !=
nullptr && prop.actualFormat == 32 && prop.numItems == 1)
546 return jmin ((
int) prop.data[0], (
int) XWindowSystemUtilities::Atoms::DndVersion);
551 ::Window externalFindDragTargetWindow (::Window target)
556 if (isWindowDnDAware (target))
559 ::Window child, phonyWin;
563 X11Symbols::getInstance()->xQueryPointer (getDisplay(), target, &phonyWin, &child, &phony, &phony, &phony, &phony, &uphony);
565 return externalFindDragTargetWindow (child);
568 void handleDragAndDropDataReceived()
572 sendDragAndDropFinish();
575 if (! dragInfoCopy.isEmpty())
576 if (
auto* peer = getPeerFor (windowH))
577 peer->handleDragDrop (dragInfoCopy);
580 void resetDragAndDrop()
584 dragAndDropCurrentMimeType = 0;
585 dragAndDropSourceWindow = 0;
586 srcMimeTypeAtomList.
clear();
587 finishAfterDropDataReceived =
false;
591 ::Window windowH = 0, targetWindow = 0, dragAndDropSourceWindow = 0;
593 int xdndVersion = -1;
594 bool isText =
false, dragging =
false, expectingStatus =
false, canDrop =
false, finishAfterDropDataReceived =
false;
596 Atom dragAndDropCurrentMimeType;