Blame docs/dnd_internals.txt

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