Transferring expensive data via Drag-and-Drop: The Delayed Transfer protocol for the X Window System [proposal]
Introduction
The Delayed Transfer protocol (XDT) builds on top of the
XDND protocol to allow clients to transfer data that is large in size or takes time to generate without blocking the X server. In particular, users should be able to initiate
XDND transfers while an XDT transfer is ongoing.
Current version: 0.0
Last updated: June 5, 2006
Rationale
A major failing of the
XDND protocol is that only one
XDND transfer can be ongoing at any one time. This is because it uses the X selection protocol; if another client takes ownership of
XdndSelection while the drag is ongoing, then the requestor's
XConvertSelection request will be passed to the wrong window.
Most implementors work around this by grabbing the pointer and mouse until
XdndFinished is received by the source. This is obviously unsatisfactory: the drag data may take time to prepare (for example, dragging files from an archiving program) or to transfer (sending large files via
XDirectSave).
Example walk-through
Step 0:
There is no step 0.
Step 1:
When sending
XdndEnter,
XdndDelayedTransfer is set on
XdndTypeList and/or in the target list fields; the
XdndDelayedTransferTypeList property is set to a list of data types which the client is capable of generating; otherwise, the
XdndTypeList is set to those data types which the client is capable of sending immediately. This allows the client to generate and cache data while the drag is ongoing, and update the
XdndTypeList appropriately. The
XdndDelayedTransferTypeList need not be a superset of the
XdndTypeList; it is suggested but not required that the
XdndDelayedTransferTypeList only contain data types that have not yet been generated or will take time to transfer.
Step 2:
When the destination receives
XdndPosition, it checks for the
XdndDelayedTransfer target and, if present, for the
XdndDelayedTransferTypeList property. It uses this in addition to the
XdndTypeList to determine whether it will accept the drop.
Step 3:
If the destination receives
XdndDrop and decides to use a delayed transfer, it requests
XdndDelayedTransfer from the
XdndSelection selection. It then indicates visually that a transfer is ongoing and sends
XdndFinished. The source receives
XdndFinished and indicates visually that a transfer is ongoing. Now the fun starts...
Step 3a:
In Step 3, both source and destination record the ongoing XDT transfer in a stack. The timestamp of the
XConvertSelection is used thereon to unambiguously identify the transfer to both sides.
Step 4:
The destination uses
SendEvent to send
SelectionRequest to the source, specifying the requested data type and a selection of
XdndDelayedTransferSelection. The time is set to the identifying timestamp. The property is chosen to avoid collision if more than one XDT transfer is running on the destination. It is suggested that clients use
XdndDelayedTransfer0, etc. and toolkits use e.g. GDK_DND_DELAYED_TRANSFER_0, etc. This reduces Atom pollution while avoiding client/toolkit nastiness.
Step 5:
The source receives
SelectionRequest, looking up the timestamp to discover which transfer is being referred to. If the source is unable to fulfil the request it responds with
SelectionNotify with a property argument of None. The source then begins to prepare the data for transfer.
Step 6:
If the destination fails to receive any response from the source for 300 seconds it assumes that the source is dead. It sends
XdndDelayedTransferFinished to the source and tears down any associated resources.
Step 7:
If the data takes more than a trivial amount of time to prepare, and at least every 10 seconds, the source notifies the destination of its progress with
XdndDelayedTransferProgress. If preparing the data fails the source sends
SelectionNotify with a property argument of None.
Step 8:
When the data is ready, the source stores the data on the destination and sends
SelectionNotify. When the destination receives
SelectionNotify it deletes the property. At any point the destination may continue to request data.
Step 9:
When the destination is finished requesting data, it sends
XdndDelayedTransferFinished. Both sides tear down resources and indicate that the transfer is finished.