Blame docs/dnd_internals.txt

Packit 98cdb6
This document describes some of the internals of the DND handling
Packit 98cdb6
code.
Packit 98cdb6
Packit 98cdb6
Organization
Packit 98cdb6
============
Packit 98cdb6
Packit 98cdb6
The DND code is split between a lowlevel part - gdkdnd.c and a
Packit 98cdb6
highlevel part - gtkdnd.c.  To put it simply, gdkdnd.c contain the
Packit 98cdb6
portions of DND code that are easiest to do in raw X, while gtkdnd.c
Packit 98cdb6
contains the portions of DND that are easiest to do with an event loop
Packit 98cdb6
and high level selection handling.
Packit 98cdb6
Packit 98cdb6
Except for a few details of selection handling, most of the
Packit 98cdb6
dependencies on the DND protocol are confined to gdkdnd.c.
Packit 98cdb6
There are two or three supported protocols - Motif DND,
Packit 98cdb6
Xdnd and a pseudo-protocol ROOTWIN, which is used for drops
Packit 98cdb6
on root windows that aren't really accepting drops.
Packit 98cdb6
gdkdnd.c divides into 4 pieces:
Packit 98cdb6
Packit 98cdb6
 1) Utility functions (finding client windows)
Packit 98cdb6
 2) Motif specific code (the biggest chunk)
Packit 98cdb6
 3) Xdnd specific code
Packit 98cdb6
 4) The public interfaces
Packit 98cdb6
Packit 98cdb6
The code in gtkdnd.c roughly consists of three parts  
Packit 98cdb6
  
Packit 98cdb6
 1) General utility functions
Packit 98cdb6
 2) Destination side code
Packit 98cdb6
 3) Source side code.
Packit 98cdb6
Packit 98cdb6
Both on the source and dest side, there is some division
Packit 98cdb6
between the low level layers and the default handlers,
Packit 98cdb6
though they are rather mixed in many cases.
Packit 98cdb6
Packit 98cdb6
Structures and Memory Management
Packit 98cdb6
================================
Packit 98cdb6
Packit 98cdb6
Information about source sites and drop sites is stored
Packit 98cdb6
in the structures GtkSourceSite and GtkDestSite.
Packit 98cdb6
Packit 98cdb6
Information about in-progress drags and drops is stored
Packit 98cdb6
in the structures GtkSourceInfo and GtkDestInfo.
Packit 98cdb6
Packit 98cdb6
The GtkSourceInfo structure is created when the drag
Packit 98cdb6
begins, and persists until the drag either completes
Packit 98cdb6
or times out. A pointer to it is stored in 
Packit 98cdb6
dataset-data for the GdkDragContext, however there
Packit 98cdb6
is no ownership. If the SourceInfo is destroyed
Packit 98cdb6
before the context, the field is simply cleared.
Packit 98cdb6
Packit 98cdb6
A GtkDestInfo is attached to each GdkDragContext
Packit 98cdb6
that is received for an incoming drag. In contrast
Packit 98cdb6
to the SourceInfo the DestInfo is "owned" by the
Packit 98cdb6
context, and when the context is destroyed, destroyed.
Packit 98cdb6
Packit 98cdb6
The GDK API
Packit 98cdb6
===========
Packit 98cdb6
Packit 98cdb6
It is expect that the GDK DND API will never be
Packit 98cdb6
used by anything other than the DND code in GTK+.
Packit 98cdb6
Packit 98cdb6
/* Drag and Drop */
Packit 98cdb6
Packit 98cdb6
GdkDragContext * gdk_drag_context_new        (void);
Packit 98cdb6
Packit 98cdb6
These create and refcount GdkDragContexts in a
Packit 98cdb6
straightforward manner.
Packit 98cdb6
Packit 98cdb6
/* Destination side */
Packit 98cdb6
Packit 98cdb6
void             gdk_drag_status        (GdkDragContext   *context,
Packit 98cdb6
				         GdkDragAction     action,
Packit 98cdb6
					 guint32           time);
Packit 98cdb6
void             gdk_drop_reply         (GdkDragContext   *context,
Packit 98cdb6
					 gboolean          ok,
Packit 98cdb6
					 guint32           time);
Packit 98cdb6
void             gdk_drop_finish        (GdkDragContext   *context,
Packit 98cdb6
					 gboolean          success,
Packit 98cdb6
					 guint32           time);
Packit 98cdb6
GdkAtom          gdk_drag_get_selection (GdkDragContext   *context);
Packit 98cdb6
Packit 98cdb6
/* Source side */
Packit 98cdb6
Packit 98cdb6
GdkDragContext * gdk_drag_begin      (GdkWindow      *window,
Packit 98cdb6
				      GList          *targets,
Packit 98cdb6
				      GdkDragAction   actions);
Packit 98cdb6
gboolean         gdk_drag_get_protocol (guint32          xid,
Packit 98cdb6
					GdkDragProtocol *protocol);
Packit 98cdb6
void             gdk_drag_find_window (GdkDragContext   *context,
Packit 98cdb6
				       GdkWindow       *drag_window,
Packit 98cdb6
			 	       gint             x_root,
Packit 98cdb6
				       gint             y_root,
Packit 98cdb6
				       GdkWindow      **dest_window,
Packit 98cdb6
				       GdkDragProtocol *protocol);
Packit 98cdb6
gboolean        gdk_drag_motion      (GdkDragContext *context,
Packit 98cdb6
				      GdkWindow      *dest_window,
Packit 98cdb6
				      GdkDragProtocol protocol,
Packit 98cdb6
				      gint            x_root, 
Packit 98cdb6
				      gint            y_root,
Packit 98cdb6
				      GdkDragAction   action,
Packit 98cdb6
				      guint32         time);
Packit 98cdb6
void            gdk_drag_drop        (GdkDragContext *context,
Packit 98cdb6
				      guint32         time);
Packit 98cdb6
void            gdk_drag_abort       (GdkDragContext *context,
Packit 98cdb6
				      guint32         time);
Packit 98cdb6
Packit 98cdb6
GdkAtom       gdk_drag_get_selection (GdkDragContext *context);
Packit 98cdb6
Packit 98cdb6
Retrieves the selection that will be used to communicate
Packit 98cdb6
the data for the drag context (valid on both source
Packit 98cdb6
and dest sides)
Packit 98cdb6
Packit 98cdb6
Cursors and window hierarchies
Packit 98cdb6
==============================
Packit 98cdb6
Packit 98cdb6
The DND code, when possible (and it isn't possible over
Packit 98cdb6
Motif window) uses a shaped window as a drag icon.
Packit 98cdb6
Because the cursor may fall inside this window during the
Packit 98cdb6
drag, we actually have to figure out which window
Packit 98cdb6
the cursor is in _ourselves_ so we can ignore the
Packit 98cdb6
drag icon properly. (Oh for OutputOnly windows!)
Packit 98cdb6
Packit 98cdb6
To avoid obscene amounts of server traffic (which are only
Packit 98cdb6
slightly observable locally, but would really kill a
Packit 98cdb6
session over a slow link), the code in GDK does
Packit 98cdb6
XGetWindowAttributes for every child of the root window at
Packit 98cdb6
the beginning of the drag, then selects with
Packit 98cdb6
SubstructureNotifyMask on the root window, so that
Packit 98cdb6
it can update this list.
Packit 98cdb6
Packit 98cdb6
It probably would be easier to just reread the entire
Packit 98cdb6
list when one of these events occurs, instead of 
Packit 98cdb6
incrementally updating, but updating the list in
Packit 98cdb6
sync was sort of fun code, so I did it that way ;-)
Packit 98cdb6
Packit 98cdb6
There is also a problem of trying to follow the
Packit 98cdb6
mouse cursor as well as possible. Currently, the
Packit 98cdb6
code uses PointerMotionHint, and an XQueryPointer
Packit 98cdb6
on MotionNotify events. This results in pretty
Packit 98cdb6
good syncing, but may result in somewhat poor
Packit 98cdb6
accuracy for drops. (Because the coordinates of
Packit 98cdb6
the drop are the coordinates when the server receives
Packit 98cdb6
the button press, which might actually be before
Packit 98cdb6
the XQueryPointer for the previous MotionNotify
Packit 98cdb6
event is done.)
Packit 98cdb6
Packit 98cdb6
Probably better is doing MotionNotify compression 
Packit 98cdb6
and discarding MotionNotify events when there
Packit 98cdb6
are more on the queue before the next ButtonPress/Release.
Packit 98cdb6
Packit 98cdb6
Proxying
Packit 98cdb6
========
Packit 98cdb6
Packit 98cdb6
A perhaps rather unusual feature of GTK's DND is proxying. A
Packit 98cdb6
dest site can be specified as a proxy drop site for another
Packit 98cdb6
window. This is most needed for the plug-socket code - the
Packit 98cdb6
socket needs to pass on drags to the plug since the original
Packit 98cdb6
source only sees toplevel windows. However, it can also be
Packit 98cdb6
used as a user visible proxy - i.e., dragging to buttons on
Packit 98cdb6
the taskbar.
Packit 98cdb6
Packit 98cdb6
Internally, when the outer drag enters a proxy dest site, a
Packit 98cdb6
new source drag is created, with SourceInfo and
Packit 98cdb6
GdkDragContext. From the GDK side, it looks much like a
Packit 98cdb6
normal source drag; on the GTK+ side, most of the code is
Packit 98cdb6
disjoint. The need to pass in a specific target window
Packit 98cdb6
is the reason why the GDK DND API splits
Packit 98cdb6
gdk_drag_find_window() and gdk_drag_motion().
Packit 98cdb6
Packit 98cdb6
For proxy drags, the GtkDestInfo and GtkSourceInfo for the
Packit 98cdb6
drag point at each other.
Packit 98cdb6
Packit 98cdb6
Because the abstraction of the drag protocol is at the GDK
Packit 98cdb6
level, a proxy drag from Motif to Xdnd or vice versa happens
Packit 98cdb6
pretty much automatically during the drag, though the
Packit 98cdb6
drop can get complicated. For Xdnd <-> Motif,
Packit 98cdb6
Motif <-> Xdnd, or Motif <-> Motif drags, it is necessary to 
Packit 98cdb6
for the Proxy to retrieve the data and pass it on to
Packit 98cdb6
the true destination, since either the selection names
Packit 98cdb6
differ or (Motif<->Motif), the proxy needs to know
Packit 98cdb6
about the XmDRAG_SUCCESS/FAILURE selection targets.
Packit 98cdb6
Packit 98cdb6
Further Reading:
Packit 98cdb6
================
Packit 98cdb6
Packit 98cdb6
Xdnd:
Packit 98cdb6
Packit 98cdb6
The spec is at:
Packit 98cdb6
Packit 98cdb6
 http://www.cco.caltech.edu/~jafl/xdnd/
Packit 98cdb6
Packit 98cdb6
Motif:
Packit 98cdb6
Packit 98cdb6
The Motif DND protocol is best described in the 
Packit 98cdb6
Hungry Programmers _Inside Lesstif_ book, available
Packit 98cdb6
from:
Packit 98cdb6
Packit 98cdb6
  http://www.igpm.rwth-aachen.de/~albrecht/hungry.html
Packit 98cdb6
Packit 98cdb6
Harald Albrecht and Mitch Miers have done a far
Packit 98cdb6
better job at documenting the DND protocol then
Packit 98cdb6
anything the OpenGroup has produced.
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Owen Taylor
Packit 98cdb6
otaylor@redhat.com
Packit 98cdb6
Oct 18, 1998