Drag-and-Drop Protocol for the X Window System
Theory
Every part of this protocol serves a purpose:
XdndAware
In order for the user to be able to transfer data from any application to any other application via DND, every application that supports XDND version N must also support all previous versions (3 to N-1). The XdndAware property provides the highest version number supported by the target (Nt). If the source supports versions up to Ns, then the version that will actually be used is min(Ns,Nt). This is the version sent in the XdndEnter message. It is important to note that XdndAware allows this to be calculated before any messages are actually sent.
It is also critical for coexisting with other DND protocols (since one can try something else if XdndAware is not present) and is useful for debugging since it lets one check the target's XDND version, after which one can expect to receive an XdndStatus message.
X Selection
By using XConvertSelection(), one can use the same data conversion code for both the Clipboard and Drag-and-Drop. This is an especially large saving if the target requests the type "MULTIPLE" or if the source is forced to send the data incrementally (type "INCR"). It also makes checking the data independent of the main sequence of messages, so XdndStatus correctly reports "yes" or "no" the first time.
By using XdndSelection, the dropped data doesn't interfere with the clipboard data stored in XA_PRIMARY.
Using XConvertSelection() does have the problem that the user can begin dragging something else before the data transfer is complete. However, the X clipboard has the same problem, so this doesn't impose any additional constraints on the user, and it can be avoided as explained below in the discussion of the XdndFinished message.
Actions
Specifying the action separately from the data type allows one to avoid defining N×M atoms for N data types and M actions. Since the user must have full control of what will happen, exactly one action is specified by the source. This is passed in the XdndPosition message to allow it to change during the drag. (e.g. if the user presses or releases modifier keys) The action accepted by the target is passed back in the XdndStatus message to allow the source to provide feedback in the cursor.
The special action XdndActionAsk tells the target that it should ask the user what to do after the drop occurs. This allows one to implement the effect obtained by right-dragging in Windows95®, where the file manager asks the user whether to move, copy, create a link, or cancel. The list of actions is retrieved from the XdndActionList property, and the description of each action that is shown to the user is retrieved from the XdndActionDescription property, both on the source window. Note that the user should be asked before retrieving the data and thus also before sending XdndFinished.
The special action XdndActionPrivate tells the source that the target will do something that the source doesn't understand and that won't require anything from the source other than a copy of the data.
Messages
The XdndEnter message initiates the session and gives the target a chance to set up local variables such as the transformation from root window coordinates to target window coordinates. It also provides a list of supported data types so the target doesn't have to call XConvertSelection() for XdndSelection, TARGETS.
The XdndPosition message provides mouse locations so that the target does not have to query the X server in order to redraw itself properly. There is no other reliable way for the target to get the mouse location because X will force the cursor to be grabbed by the source window, so only the source window will be receiving events. The target needs the mouse location because it has to update itself to show where the data will be inserted. This is especially important in text editors, spreadsheets, and file managers.
The time stamp in the XdndPosition message must be passed to XConvertSelection() to ensure that the correct data is received.
The scroll request in the XdndPosition message provides one way to scroll the target. Ideally, the raw state of the X event should be sent to the target to allow it to behave exactly the same way as if the user were not performing DND. However, since the source must unilaterally decide which X events should trigger a scroll request, the source and target must agree on the same trigger events. The de facto standard triggers are the ButtonPress events from the mousewheel: buttons 4,5,6,7. This is why the XdndPosition message includes both the index of the button and the complete state of all the modifier keys.
All targets should also provide scroll zones along the edges to allow users without a mousewheel to scroll the target.
The XdndStatus message provides feedback to the source (e.g. it might want to change the cursor) and ensures that XdndPosition messages do not pile up when the network connection is slow.
The XdndLeave message cancels the session.
The XdndDrop message tells the target to proceed with the drop. The time stamp must be passed to XConvertSelection() to ensure that the correct data is received.
The XdndFinished message tells the source that the target is done and no longer needs the data. This allows the source to implement any one of three different behaviors:
-
Block until the message is received. In this case, the source must be prepared to time out in case the target malfunctions and must reject outdated requests.
-
Don't block and reject outdated requests by comparing the time when the selection was last acquired with the timestamp in the selection request. (which comes from the XdndDrop message)
-
Don't block and keep a history of previous data. This can be very difficult to implement, but it is clearly the ideal behavior from the user's perspective because it allows him to drop something and then continue working with the assurance that the target will get the data regardless of how slow the network connections are.
When the source receives XdndFinished, it can remove the item from its history, thereby keeping it from getting too large. The source must also be prepared to throw out extremely old data in case a target malfunctions.
Protecting against malfunctioning programs
If the version number in the XdndEnter message is higher than what the target can support, the target should ignore the source.
While the source and target are receiving XDND messages from each other, they should ignore all XDND messages from other windows.
If either application crashes while DND is active, the other application must avoid crashing on a BadWindow error. The only safe way to do this is to actually catch the error by installing an error handler with XSetErrorHandler(). In addition, the target must also listen for DestroyNotify events so that it doesn't wait forever for another XdndPosition if the source crashes between receiving XdndStatus and sending XdndPosition.
-
If the target crashes, the source will automatically receive another EnterNotify event, as if the mouse had moved. Any XdndPosition in the network will generate a BadWindow error.
-
If the source crashes, the target should treat it like XdndLeave.
As discussed above, the source must be careful to avoid locking up if the target does not send XdndFinished.