source: trunk/third/gtk/gdk/gdkdnd.c @ 14810

Revision 14810, 83.0 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14809, which included commits to RCS files with non-trunk default branches.
Line 
1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20/*
21 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22 * file for a list of people on the GTK+ Team.  See the ChangeLog
23 * files for a list of changes.  These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25 */
26
27#include <X11/Xlib.h>
28#include <X11/Xatom.h>
29#include <string.h>
30#include "gdkx.h"
31#include "gdk/gdkprivate.h"
32#include "gdk.h"
33
34typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
35
36typedef enum {
37  GDK_DRAG_STATUS_DRAG,
38  GDK_DRAG_STATUS_MOTION_WAIT,
39  GDK_DRAG_STATUS_ACTION_WAIT,
40  GDK_DRAG_STATUS_DROP
41} GtkDragStatus;
42
43typedef struct {
44  guint32 xid;
45  gint x, y, width, height;
46  gboolean mapped;
47} GdkCacheChild;
48
49typedef struct {
50  GList *children;
51  GHashTable *child_hash;
52  guint old_event_mask;
53} GdkWindowCache;
54
55/* Structure that holds information about a drag in progress.
56 * this is used on both source and destination sides.
57 */
58struct _GdkDragContextPrivate {
59  GdkDragContext context;
60
61  GdkAtom motif_selection;
62  GdkAtom xdnd_selection;
63  guint   ref_count;
64
65  guint16 last_x;               /* Coordinates from last event */
66  guint16 last_y;
67  GdkDragAction old_action;       /* The last action we sent to the source */
68  GdkDragAction old_actions;      /* The last actions we sent to the source */
69  GdkDragAction xdnd_actions;     /* What is currently set in XdndActionList */
70
71  Window dest_xid;              /* The last window we looked up */
72  Window drop_xid;            /* The (non-proxied) window that is receiving drops */
73  guint xdnd_targets_set : 1;   /* Whether we've already set XdndTypeList */
74  guint xdnd_actions_set : 1;   /* Whether we've already set XdndActionList */
75  guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
76  guint motif_targets_set : 1;  /* Whether we've already set motif initiator info */
77  guint drag_status : 4;        /* current status of drag */
78
79  GdkWindowCache *window_cache;
80};
81
82GdkDragContext *current_dest_drag = NULL;
83
84/* Forward declarations */
85
86static void gdk_window_cache_destroy (GdkWindowCache *cache);
87
88static void     motif_read_target_table (void);
89static GdkFilterReturn motif_dnd_filter (GdkXEvent *xev,
90                                         GdkEvent  *event,
91                                         gpointer   data);
92
93static GdkFilterReturn xdnd_enter_filter (GdkXEvent *xev,
94                                          GdkEvent  *event,
95                                          gpointer   data);
96
97static GdkFilterReturn xdnd_leave_filter (GdkXEvent *xev,
98                                          GdkEvent  *event,
99                                          gpointer   data);
100
101static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
102                                             GdkEvent  *event,
103                                             gpointer   data);
104
105static GdkFilterReturn xdnd_status_filter (GdkXEvent *xev,
106                                           GdkEvent  *event,
107                                           gpointer   data);
108
109static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
110                                            GdkEvent  *event,
111                                            gpointer   data);
112
113static GdkFilterReturn xdnd_drop_filter (GdkXEvent *xev,
114                                         GdkEvent  *event,
115                                         gpointer   data);
116
117static void   xdnd_manage_source_filter (GdkDragContext *context,
118                                         GdkWindow      *window,
119                                         gboolean        add_filter);
120
121/* Drag Contexts */
122
123static GList *contexts;
124
125GdkDragContext *
126gdk_drag_context_new        (void)
127{
128  GdkDragContextPrivate *result;
129
130  result = g_new0 (GdkDragContextPrivate, 1);
131
132  result->ref_count = 1;
133
134  contexts = g_list_prepend (contexts, result);
135
136  return (GdkDragContext *)result;
137}
138
139void           
140gdk_drag_context_ref (GdkDragContext *context)
141{
142  g_return_if_fail (context != NULL);
143
144  ((GdkDragContextPrivate *)context)->ref_count++;
145}
146
147void           
148gdk_drag_context_unref (GdkDragContext *context)
149{
150  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
151
152  g_return_if_fail (context != NULL);
153  g_return_if_fail (private->ref_count > 0);
154
155  private->ref_count--;
156 
157  if (private->ref_count == 0)
158    {
159      g_dataset_destroy (private);
160     
161      g_list_free (context->targets);
162
163      if (context->source_window)
164        {
165          if ((context->protocol == GDK_DRAG_PROTO_XDND) &&
166              !context->is_source)
167            xdnd_manage_source_filter (context, context->source_window, FALSE);
168
169          gdk_window_unref (context->source_window);
170        }
171
172      if (context->dest_window)
173        gdk_window_unref (context->dest_window);
174
175      if (private->window_cache)
176        gdk_window_cache_destroy (private->window_cache);
177
178      contexts = g_list_remove (contexts, private);
179      g_free (private);
180    }
181}
182
183static GdkDragContext *
184gdk_drag_context_find (gboolean is_source,
185                       Window   source_xid,
186                       Window   dest_xid)
187{
188  GList *tmp_list = contexts;
189  GdkDragContext *context;
190  GdkDragContextPrivate *private;
191  Window context_dest_xid;
192
193  while (tmp_list)
194    {
195      context = (GdkDragContext *)tmp_list->data;
196      private = (GdkDragContextPrivate *)context;
197
198      context_dest_xid = context->dest_window ?
199                           (private->drop_xid ?
200                              private->drop_xid :
201                              GDK_WINDOW_XWINDOW (context->dest_window)) :
202                           None;
203
204      if ((!context->is_source == !is_source) &&
205          ((source_xid == None) || (context->source_window &&
206            (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
207          ((dest_xid == None) || (context_dest_xid == dest_xid)))
208        return context;
209     
210      tmp_list = tmp_list->next;
211    }
212 
213  return NULL;
214}
215
216/* Utility functions */
217
218static void
219gdk_window_cache_add (GdkWindowCache *cache,
220                      guint32 xid,
221                      gint x, gint y, gint width, gint height,
222                      gboolean mapped)
223{
224  GdkCacheChild *child = g_new (GdkCacheChild, 1);
225
226  child->xid = xid;
227  child->x = x;
228  child->y = y;
229  child->width = width;
230  child->height = height;
231  child->mapped = mapped;
232
233  cache->children = g_list_prepend (cache->children, child);
234  g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
235                       cache->children);
236}
237
238static GdkFilterReturn
239gdk_window_cache_filter (GdkXEvent *xev,
240                         GdkEvent  *event,
241                         gpointer   data)
242{
243  XEvent *xevent = (XEvent *)xev;
244  GdkWindowCache *cache = data;
245
246  switch (xevent->type)
247    {
248    case CirculateNotify:
249      break;
250    case ConfigureNotify:
251      {
252        XConfigureEvent *xce = &xevent->xconfigure;
253        GList *node;
254
255        node = g_hash_table_lookup (cache->child_hash,
256                                    GUINT_TO_POINTER (xce->window));
257        if (node)
258          {
259            GdkCacheChild *child = node->data;
260            child->x = xce->x;
261            child->y = xce->y;
262            child->width = xce->width;
263            child->height = xce->height;
264            if (xce->above == None && (node->next))
265              {
266                GList *last = g_list_last (cache->children);
267                cache->children = g_list_remove_link (cache->children, node);
268                last->next = node;
269                node->next = NULL;
270                node->prev = last;
271              }
272            else
273              {
274                GList *above_node = g_hash_table_lookup (cache->child_hash,
275                                                         GUINT_TO_POINTER (xce->above));
276                if (above_node && node->prev != above_node)
277                  {
278                    cache->children = g_list_remove_link (cache->children, node);
279                    node->next = above_node->next;
280                    if (node->next)
281                      node->next->prev = node;
282                    node->prev = above_node;
283                    above_node->next = node;
284                  }
285              }
286          }
287        break;
288      }
289    case CreateNotify:
290      {
291        XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
292
293        if (!g_hash_table_lookup (cache->child_hash,
294                                  GUINT_TO_POINTER (xcwe->window)))
295          gdk_window_cache_add (cache, xcwe->window,
296                                xcwe->x, xcwe->y, xcwe->width, xcwe->height,
297                                FALSE);
298        break;
299      }
300    case DestroyNotify:
301      {
302        XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
303        GList *node;
304
305        node = g_hash_table_lookup (cache->child_hash,
306                                    GUINT_TO_POINTER (xdwe->window));
307        if (node)
308          {
309            g_hash_table_remove (cache->child_hash,
310                                 GUINT_TO_POINTER (xdwe->window));
311            cache->children = g_list_remove_link (cache->children, node);
312            g_free (node->data);
313            g_list_free_1 (node);
314          }
315        break;
316      }
317    case MapNotify:
318      {
319        XMapEvent *xme = &xevent->xmap;
320        GList *node;
321
322        node = g_hash_table_lookup (cache->child_hash,
323                                    GUINT_TO_POINTER (xme->window));
324        if (node)
325          {
326            GdkCacheChild *child = node->data;
327            child->mapped = TRUE;
328          }
329        break;
330      }
331    case ReparentNotify:
332      break;
333    case UnmapNotify:
334      {
335        XMapEvent *xume = &xevent->xmap;
336        GList *node;
337
338        node = g_hash_table_lookup (cache->child_hash,
339                                    GUINT_TO_POINTER (xume->window));
340        if (node)
341          {
342            GdkCacheChild *child = node->data;
343            child->mapped = FALSE;
344          }
345        break;
346      }
347    default:
348      return GDK_FILTER_CONTINUE;
349    }
350  return GDK_FILTER_REMOVE;
351}
352
353static GdkWindowCache *
354gdk_window_cache_new (void)
355{
356  XWindowAttributes xwa;
357  Window root, parent, *children;
358  unsigned int nchildren;
359  int i;
360 
361  gint old_warnings = gdk_error_warnings;
362 
363  GdkWindowCache *result = g_new (GdkWindowCache, 1);
364
365  result->children = NULL;
366  result->child_hash = g_hash_table_new (g_direct_hash, NULL);
367
368  XGetWindowAttributes (gdk_display, gdk_root_window, &xwa);
369  result->old_event_mask = xwa.your_event_mask;
370  XSelectInput (gdk_display, gdk_root_window,
371                result->old_event_mask | SubstructureNotifyMask);
372  gdk_window_add_filter ((GdkWindow *)&gdk_root_parent,
373                         gdk_window_cache_filter, result);
374 
375  gdk_error_code = 0;
376  gdk_error_warnings = 0;
377
378  if (XQueryTree(gdk_display, gdk_root_window,
379                 &root, &parent, &children, &nchildren) == 0)
380    return result;
381 
382  for (i = 0; i < nchildren ; i++)
383    {
384      XGetWindowAttributes (gdk_display, children[i], &xwa);
385
386      gdk_window_cache_add (result, children[i],
387                            xwa.x, xwa.y, xwa.width, xwa.height,
388                            xwa.map_state != IsUnmapped);
389
390      if (gdk_error_code)
391        gdk_error_code = 0;
392      else
393        {
394          gdk_window_cache_add (result, children[i],
395                                xwa.x, xwa.y, xwa.width, xwa.height,
396                                (xwa.map_state != IsUnmapped));
397        }
398    }
399
400  XFree (children);
401
402  gdk_error_warnings = old_warnings;
403
404  return result;
405}
406
407static void
408gdk_window_cache_destroy (GdkWindowCache *cache)
409{
410  XSelectInput (gdk_display, gdk_root_window, cache->old_event_mask);
411  gdk_window_remove_filter ((GdkWindow *)&gdk_root_parent,
412                            gdk_window_cache_filter, cache);
413
414  g_list_foreach (cache->children, (GFunc)g_free, NULL);
415  g_list_free (cache->children);
416  g_hash_table_destroy (cache->child_hash);
417
418  g_free (cache);
419}
420
421static Window
422get_client_window_at_coords_recurse (Window  win,
423                                     gint    x,
424                                     gint    y)
425{
426  Window root, tmp_parent, *children;
427  unsigned int nchildren;
428  int i;
429  Window child = None;
430  Atom type = None;
431  int format;
432  unsigned long nitems, after;
433  unsigned char *data;
434 
435  static Atom wm_state_atom = None;
436
437  if (!wm_state_atom)
438    wm_state_atom = gdk_atom_intern ("WM_STATE", FALSE);
439   
440  XGetWindowProperty (gdk_display, win,
441                      wm_state_atom, 0, 0, False, AnyPropertyType,
442                      &type, &format, &nitems, &after, &data);
443 
444  if (gdk_error_code)
445    {
446      gdk_error_code = 0;
447
448      return None;
449    }
450
451  if (type != None)
452    {
453      XFree (data);
454      return win;
455    }
456
457#if 0
458  /* This is beautiful! Damn Enlightenment and click-to-focus */
459  XTranslateCoordinates (gdk_display, gdk_root_window, win,
460                         x_root, y_root, &dest_x, &dest_y, &child);
461
462  if (gdk_error_code)
463    {
464      gdk_error_code = 0;
465
466      return None;
467    }
468 
469#else
470  if (XQueryTree(gdk_display, win,
471                 &root, &tmp_parent, &children, &nchildren) == 0)
472    return 0;
473
474  if (!gdk_error_code)
475    {
476      for (i = nchildren - 1; (i >= 0) && (child == None); i--)
477        {
478          XWindowAttributes xwa;
479         
480          XGetWindowAttributes (gdk_display, children[i], &xwa);
481         
482          if (gdk_error_code)
483            gdk_error_code = 0;
484          else if ((xwa.map_state == IsViewable) && (xwa.class == InputOutput) &&
485                   (x >= xwa.x) && (x < xwa.x + (gint)xwa.width) &&
486                   (y >= xwa.y) && (y < xwa.y + (gint)xwa.height))
487            {
488              x -= xwa.x;
489              y -= xwa.y;
490              child = children[i];
491            }
492        }
493     
494      XFree (children);
495    }
496  else
497    gdk_error_code = 0;
498#endif 
499
500  if (child)
501    return get_client_window_at_coords_recurse (child, x, y);
502  else
503    return None;
504}
505
506static Window
507get_client_window_at_coords (GdkWindowCache *cache,
508                             Window          ignore,
509                             gint            x_root,
510                             gint            y_root)
511{
512  GList *tmp_list;
513  Window retval = None;
514
515  gint old_warnings = gdk_error_warnings;
516 
517  gdk_error_code = 0;
518  gdk_error_warnings = 0;
519
520  tmp_list = cache->children;
521
522  while (tmp_list && !retval)
523    {
524      GdkCacheChild *child = tmp_list->data;
525
526      if ((child->xid != ignore) && (child->mapped))
527        {
528          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
529              (y_root >= child->y) && (y_root < child->y + child->height))
530            {
531              retval = get_client_window_at_coords_recurse (child->xid,
532                                                            x_root - child->x,
533                                                            y_root - child->y);
534              if (!retval)
535                retval = child->xid;
536            }
537         
538        }
539      tmp_list = tmp_list->next;
540    }
541
542  gdk_error_warnings = old_warnings;
543  if (retval)
544    return retval;
545  else
546    return gdk_root_window;
547}
548
549#if 0
550static Window
551get_client_window_at_coords_recurse (Window  win,
552                                     gint    x_root,
553                                     gint    y_root)
554{
555  Window child;
556  Atom type = None;
557  int format;
558  unsigned long nitems, after;
559  unsigned char *data;
560  int dest_x, dest_y;
561 
562  static Atom wm_state_atom = None;
563
564  if (!wm_state_atom)
565    wm_state_atom = gdk_atom_intern ("WM_STATE", FALSE);
566   
567  XGetWindowProperty (gdk_display, win,
568                      wm_state_atom, 0, 0, False, AnyPropertyType,
569                      &type, &format, &nitems, &after, &data);
570 
571  if (gdk_error_code)
572    {
573      gdk_error_code = 0;
574
575      return None;
576    }
577
578  if (type != None)
579    {
580      XFree (data);
581      return win;
582    }
583
584  XTranslateCoordinates (gdk_display, gdk_root_window, win,
585                         x_root, y_root, &dest_x, &dest_y, &child);
586
587  if (gdk_error_code)
588    {
589      gdk_error_code = 0;
590
591      return None;
592    }
593
594  if (child)
595    return get_client_window_at_coords_recurse (child, x_root, y_root);
596  else
597    return None;
598}
599
600static Window
601get_client_window_at_coords (Window  ignore,
602                             gint    x_root,
603                             gint    y_root)
604{
605  Window root, parent, *children;
606  unsigned int nchildren;
607  int i;
608  Window retval = None;
609 
610  gint old_warnings = gdk_error_warnings;
611 
612  gdk_error_code = 0;
613  gdk_error_warnings = 0;
614
615  if (XQueryTree(gdk_display, gdk_root_window,
616                 &root, &parent, &children, &nchildren) == 0)
617    return 0;
618
619  for (i = nchildren - 1; (i >= 0) && (retval == None); i--)
620    {
621      if (children[i] != ignore)
622        {
623          XWindowAttributes xwa;
624
625          XGetWindowAttributes (gdk_display, children[i], &xwa);
626
627          if (gdk_error_code)
628            gdk_error_code = 0;
629          else if ((xwa.map_state == IsViewable) &&
630                   (x_root >= xwa.x) && (x_root < xwa.x + (gint)xwa.width) &&
631                   (y_root >= xwa.y) && (y_root < xwa.y + (gint)xwa.height))
632            {
633              retval = get_client_window_at_coords_recurse (children[i],
634                                                            x_root, y_root);
635              if (!retval)
636                retval = children[i];
637            }
638        }
639    }
640
641  XFree (children);
642
643  gdk_error_warnings = old_warnings;
644  if (retval)
645    return retval;
646  else
647    return gdk_root_window;
648}
649#endif
650
651/*************************************************************
652 ***************************** MOTIF *************************
653 *************************************************************/
654
655/* values used in the message type for Motif DND */
656enum {
657    XmTOP_LEVEL_ENTER,
658    XmTOP_LEVEL_LEAVE,
659    XmDRAG_MOTION,
660    XmDROP_SITE_ENTER,
661    XmDROP_SITE_LEAVE,
662    XmDROP_START,
663    XmDROP_FINISH,
664    XmDRAG_DROP_FINISH,
665    XmOPERATION_CHANGED
666};
667
668/* Values used to specify type of protocol to use */
669enum {
670    XmDRAG_NONE,
671    XmDRAG_DROP_ONLY,
672    XmDRAG_PREFER_PREREGISTER,
673    XmDRAG_PREREGISTER,
674    XmDRAG_PREFER_DYNAMIC,
675    XmDRAG_DYNAMIC,
676    XmDRAG_PREFER_RECEIVER
677};
678
679/* Operation codes */
680enum {
681  XmDROP_NOOP,
682  XmDROP_MOVE = 0x01,
683  XmDROP_COPY = 0x02,
684  XmDROP_LINK = 0x04
685};
686
687/* Drop site status */
688enum {
689  XmNO_DROP_SITE = 0x01,
690  XmDROP_SITE_INVALID = 0x02,
691  XmDROP_SITE_VALID = 0x03
692};
693
694/* completion status */
695enum {
696  XmDROP,
697  XmDROP_HELP,
698  XmDROP_CANCEL,
699  XmDROP_INTERRUPT
700};
701
702/* Static data for MOTIF DND */
703static GList **motif_target_lists = NULL;
704static gint motif_n_target_lists = -1;
705
706/* Byte swapping routines. The motif specification leaves it
707 * up to us to save a few bytes in the client messages
708 */
709static gchar local_byte_order = '\0';
710
711#ifdef G_ENABLE_DEBUG
712static void
713print_target_list (GList *targets)
714{
715  while (targets)
716    {
717      gchar *name = gdk_atom_name (GPOINTER_TO_INT (targets->data));
718      g_message ("\t%s", name);
719      g_free (name);
720      targets = targets->next;
721    }
722}
723#endif /* G_ENABLE_DEBUG */
724
725static void
726init_byte_order (void)
727{
728  guint32 myint = 0x01020304;
729  local_byte_order = (*(gchar *)&myint == 1) ? 'B' : 'l';
730}
731
732static guint16
733card16_to_host (guint16 x, gchar byte_order) {
734  if (byte_order == local_byte_order)
735    return x;
736  else
737    return (x << 8) | (x >> 8);
738}
739
740static guint32
741card32_to_host (guint32 x, gchar byte_order) {
742  if (byte_order == local_byte_order)
743    return x;
744  else
745    return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
746}
747
748/* Motif packs together fields of varying length into the
749 * client message. We can't rely on accessing these
750 * through data.s[], data.l[], etc, because on some architectures
751 * (i.e., Alpha) these won't be valid for format == 8.
752 */
753
754#define MOTIF_XCLIENT_BYTE(xevent,i) \
755  (xevent)->xclient.data.b[i]
756#define MOTIF_XCLIENT_SHORT(xevent,i) \
757  ((gint16 *)&((xevent)->xclient.data.b[0]))[i]
758#define MOTIF_XCLIENT_LONG(xevent,i) \
759  ((gint32 *)&((xevent)->xclient.data.b[0]))[i]
760
761#define MOTIF_UNPACK_BYTE(xevent,i) MOTIF_XCLIENT_BYTE(xevent,i)
762#define MOTIF_UNPACK_SHORT(xevent,i) \
763  card16_to_host (MOTIF_XCLIENT_SHORT(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
764#define MOTIF_UNPACK_LONG(xevent,i) \
765  card32_to_host (MOTIF_XCLIENT_LONG(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
766
767/***** Dest side ***********/
768
769/* Property placed on source windows */
770typedef struct _MotifDragInitiatorInfo {
771  guint8 byte_order;
772  guint8 protocol_version;
773  guint16 targets_index;
774  guint32 selection_atom;
775} MotifDragInitiatorInfo;
776
777/* Header for target table on the drag window */
778typedef struct _MotifTargetTableHeader {
779  guchar byte_order;
780  guchar protocol_version;
781  guint16 n_lists;
782  guint32 total_size;
783} MotifTargetTableHeader;
784
785/* Property placed on target windows */
786typedef struct _MotifDragReceiverInfo {
787  guint8 byte_order;
788  guint8 protocol_version;
789  guint8 protocol_style;
790  guint8 pad;
791  guint32 proxy_window;
792  guint16 num_drop_sites;
793  guint16 padding;
794  guint32 total_size;
795} MotifDragReceiverInfo;
796
797static Window motif_drag_window = None;
798static GdkWindow *motif_drag_gdk_window = NULL;
799
800static GdkAtom motif_drag_targets_atom = GDK_NONE;
801static GdkAtom motif_drag_receiver_info_atom = GDK_NONE;
802
803/* Target table handling */
804
805GdkFilterReturn
806motif_drag_window_filter (GdkXEvent *xevent,
807                          GdkEvent  *event,
808                          gpointer data)
809{
810  XEvent *xev = (XEvent *)xevent;
811
812  switch (xev->xany.type)
813    {
814    case DestroyNotify:
815      motif_drag_window = None;
816      motif_drag_gdk_window = NULL;
817      break;
818    case PropertyNotify:
819      if (motif_target_lists &&
820          motif_drag_targets_atom &&
821          (xev->xproperty.atom == motif_drag_targets_atom))
822        motif_read_target_table();
823      break;
824    }
825  return GDK_FILTER_REMOVE;
826}
827
828static Atom motif_drag_window_atom = GDK_NONE;
829
830static Window
831motif_lookup_drag_window (Display *display)
832{
833  Window retval = None;
834  gulong bytes_after, nitems;
835  GdkAtom type;
836  gint format;
837  guchar *data;
838
839  XGetWindowProperty (gdk_display, gdk_root_window, motif_drag_window_atom,
840                      0, 1, FALSE,
841                      XA_WINDOW, &type, &format, &nitems, &bytes_after,
842                      &data);
843 
844  if ((format == 32) && (nitems == 1) && (bytes_after == 0))
845    {
846      retval = *(Window *)data;
847      GDK_NOTE(DND,
848               g_message ("Found drag window %#lx\n", motif_drag_window));
849    }
850
851  if (type != None)
852    XFree (data);
853
854  return retval;
855}
856
857/* Finds the window where global Motif drag information is stored.
858 * If it doesn't exist and 'create' is TRUE, create one.
859 */
860Window
861motif_find_drag_window (gboolean create)
862{
863  if (!motif_drag_window)
864    {
865      if (!motif_drag_window_atom)
866        motif_drag_window_atom = gdk_atom_intern ("_MOTIF_DRAG_WINDOW", TRUE);
867
868      motif_drag_window = motif_lookup_drag_window (gdk_display);
869     
870      if (!motif_drag_window && create)
871        {
872          /* Create a persistant window. (Copied from LessTif) */
873         
874          Display *display;
875          XSetWindowAttributes attr;
876          display = XOpenDisplay (gdk_display_name);
877          XSetCloseDownMode (display, RetainPermanent);
878
879          XGrabServer (display);
880         
881          motif_drag_window = motif_lookup_drag_window (display);
882
883          if (!motif_drag_window)
884            {
885              attr.override_redirect = True;
886              attr.event_mask = PropertyChangeMask;
887             
888              motif_drag_window =
889                XCreateWindow(display, DefaultRootWindow(display),
890                              -100, -100, 10, 10, 0, 0,
891                              InputOnly, CopyFromParent,
892                              (CWOverrideRedirect | CWEventMask), &attr);
893             
894              GDK_NOTE (DND,
895                        g_message ("Created drag window %#lx\n", motif_drag_window));
896             
897              XChangeProperty (display, gdk_root_window,
898                               motif_drag_window_atom, XA_WINDOW,
899                               32, PropModeReplace,
900                               (guchar *)&motif_drag_window_atom, 1);
901
902            }
903          XUngrabServer (display);
904          XCloseDisplay (display);
905        }
906
907      /* There is a miniscule race condition here if the drag window
908       * gets destroyed exactly now.
909       */
910      if (motif_drag_window)
911        {
912          motif_drag_gdk_window = gdk_window_foreign_new (motif_drag_window);
913          gdk_window_add_filter (motif_drag_gdk_window,
914                                 motif_drag_window_filter,
915                                 NULL);
916        }
917    }
918
919  return motif_drag_window;
920}
921
922static void
923motif_read_target_table (void)
924{
925  gulong bytes_after, nitems;
926  GdkAtom type;
927  gint format;
928  gint i, j;
929
930  if (!motif_drag_targets_atom)
931    motif_drag_targets_atom = gdk_atom_intern ("_MOTIF_DRAG_TARGETS", FALSE);
932
933  if (motif_target_lists)
934    {
935      for (i=0; i<motif_n_target_lists; i++)
936        g_list_free (motif_target_lists[i]);
937     
938      g_free (motif_target_lists);
939      motif_target_lists = NULL;
940      motif_n_target_lists = 0;
941    }
942
943  if (motif_find_drag_window (FALSE))
944    {
945      MotifTargetTableHeader *header = NULL;
946      guchar *target_bytes = NULL;
947      guchar *p;
948      gboolean success = FALSE;
949
950      gdk_error_trap_push ();
951      XGetWindowProperty (gdk_display, motif_drag_window,
952                          motif_drag_targets_atom,
953                          0, (sizeof(MotifTargetTableHeader)+3)/4, FALSE,
954                          motif_drag_targets_atom,
955                          &type, &format, &nitems, &bytes_after,
956                          (guchar **)&header);
957
958      if (gdk_error_trap_pop () || (format != 8) || (nitems < sizeof (MotifTargetTableHeader)))
959        goto error;
960
961      header->n_lists = card16_to_host (header->n_lists, header->byte_order);
962      header->total_size = card32_to_host (header->total_size, header->byte_order);
963
964      gdk_error_trap_push ();
965      XGetWindowProperty (gdk_display, motif_drag_window, motif_drag_targets_atom,
966                          (sizeof(MotifTargetTableHeader)+3)/4,
967                          (header->total_size + 3)/4 - (sizeof(MotifTargetTableHeader) + 3)/4,
968                          FALSE,
969                          motif_drag_targets_atom, &type, &format, &nitems,
970                          &bytes_after, &target_bytes);
971     
972      if (gdk_error_trap_pop () || (format != 8) || (bytes_after != 0) ||
973          (nitems != header->total_size - sizeof(MotifTargetTableHeader)))
974          goto error;
975
976      motif_n_target_lists = header->n_lists;
977      motif_target_lists = g_new0 (GList *, motif_n_target_lists);
978
979      p = target_bytes;
980      for (i=0; i<header->n_lists; i++)
981        {
982          gint n_targets;
983          guint32 *targets;
984         
985          if (p + sizeof(guint16) - target_bytes > nitems)
986            goto error;
987
988          n_targets = card16_to_host (*(gushort *)p, header->byte_order);
989
990          /* We need to make a copy of the targets, since it may
991           * be unaligned
992           */
993          targets = g_new (guint32, n_targets);
994          memcpy (targets, p + sizeof(guint16), sizeof(guint32) * n_targets);
995
996          p +=  sizeof(guint16) + n_targets * sizeof(guint32);
997          if (p - target_bytes > nitems)
998            goto error;
999
1000          for (j=0; j<n_targets; j++)
1001            motif_target_lists[i] =
1002              g_list_prepend (motif_target_lists[i],
1003                              GUINT_TO_POINTER (card32_to_host (targets[j],
1004                                                                header->byte_order)));
1005          g_free (targets);
1006          motif_target_lists[i] = g_list_reverse (motif_target_lists[i]);
1007        }
1008
1009      success = TRUE;
1010     
1011    error:
1012      if (header)
1013        XFree (header);
1014     
1015      if (target_bytes)
1016        XFree (target_bytes);
1017
1018      if (!success)
1019        {
1020          if (motif_target_lists)
1021            {
1022              g_free (motif_target_lists);
1023              motif_target_lists = NULL;
1024              motif_n_target_lists = 0;
1025            }
1026          g_warning ("Error reading Motif target table\n");
1027        }
1028    }
1029}
1030
1031static gint
1032targets_sort_func (gconstpointer a, gconstpointer b)
1033{
1034  return (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) ?
1035    -1 : ((GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) ? 1 : 0);
1036}
1037
1038/* Check if given (sorted) list is in the targets table */
1039static gboolean
1040motif_target_table_check (GList *sorted)
1041{
1042  GList *tmp_list1, *tmp_list2;
1043  gint i;
1044
1045  for (i=0; i<motif_n_target_lists; i++)
1046    {
1047      tmp_list1 = motif_target_lists[i];
1048      tmp_list2 = sorted;
1049     
1050      while (tmp_list1 && tmp_list2)
1051        {
1052          if (tmp_list1->data != tmp_list2->data)
1053            break;
1054
1055          tmp_list1 = tmp_list1->next;
1056          tmp_list2 = tmp_list2->next;
1057        }
1058      if (!tmp_list1 && !tmp_list2)     /* Found it */
1059        return i;
1060    }
1061
1062  return -1;
1063}
1064
1065static gint
1066motif_add_to_target_table (GList *targets)
1067{
1068  GList *sorted = NULL;
1069  gint index = -1;
1070  gint i;
1071  GList *tmp_list;
1072 
1073  /* make a sorted copy of the list */
1074 
1075  while (targets)
1076    {
1077      sorted = g_list_insert_sorted (sorted, targets->data, targets_sort_func);
1078      targets = targets->next;
1079    }
1080
1081  /* First check if it is their already */
1082
1083  if (motif_target_lists)
1084    index = motif_target_table_check (sorted);
1085
1086  /* We need to grab the server while doing this, to ensure
1087   * atomiticity. Ugh
1088   */
1089
1090  if (index < 0)
1091    {
1092      /* We need to make sure that it exists _before_ we grab the
1093       * server, since we can't open a new connection after we
1094       * grab the server.
1095       */
1096      motif_find_drag_window (TRUE);
1097
1098      XGrabServer(gdk_display);
1099      motif_read_target_table();
1100   
1101      /* Check again, in case it was added in the meantime */
1102     
1103      if (motif_target_lists)
1104        index =  motif_target_table_check (sorted);
1105
1106      if (index < 0)
1107        {
1108          guint32 total_size = 0;
1109          guchar *data;
1110          guchar *p;
1111          guint16 *p16;
1112          MotifTargetTableHeader *header;
1113         
1114          if (!motif_target_lists)
1115            {
1116              motif_target_lists = g_new (GList *, 1);
1117              motif_n_target_lists = 1;
1118            }
1119          else
1120            {
1121              motif_n_target_lists++;
1122              motif_target_lists = g_realloc (motif_target_lists,
1123                                              sizeof(GList *) * motif_n_target_lists);
1124            }
1125          motif_target_lists[motif_n_target_lists - 1] = sorted;
1126          sorted = NULL;
1127          index = motif_n_target_lists - 1;
1128
1129          total_size = sizeof (MotifTargetTableHeader);
1130          for (i = 0; i < motif_n_target_lists ; i++)
1131            total_size += sizeof(guint16) + sizeof(guint32) * g_list_length (motif_target_lists[i]);
1132
1133          data = g_malloc (total_size);
1134
1135          header = (MotifTargetTableHeader *)data;
1136          p = data + sizeof(MotifTargetTableHeader);
1137
1138          header->byte_order = local_byte_order;
1139          header->protocol_version = 0;
1140          header->n_lists = motif_n_target_lists;
1141          header->total_size = total_size;
1142
1143          for (i = 0; i < motif_n_target_lists ; i++)
1144            {
1145              guint16 n_targets = g_list_length (motif_target_lists[i]);
1146              guint32 *targets = g_new (guint32, n_targets);
1147              guint32 *p32 = targets;
1148             
1149              tmp_list = motif_target_lists[i];
1150              while (tmp_list)
1151                {
1152                  *p32 = GPOINTER_TO_UINT (tmp_list->data);
1153                 
1154                  tmp_list = tmp_list->next;
1155                  p32++;
1156                }
1157
1158              p16 = (guint16 *)p;
1159              p += sizeof(guint16);
1160
1161              memcpy (p, targets, n_targets * sizeof(guint32));
1162
1163              *p16 = n_targets;
1164              p += sizeof(guint32) * n_targets;
1165              g_free (targets);
1166            }
1167
1168          XChangeProperty (gdk_display, motif_drag_window,
1169                           motif_drag_targets_atom,
1170                           motif_drag_targets_atom,
1171                           8, PropModeReplace,
1172                           data, total_size);
1173        }
1174      XUngrabServer(gdk_display);
1175    }
1176
1177  g_list_free (sorted);
1178  return index;
1179}
1180
1181/* Translate flags */
1182
1183static void
1184motif_dnd_translate_flags (GdkDragContext *context, guint16 flags)
1185{
1186  guint recommended_op = flags & 0x000f;
1187  guint possible_ops = (flags & 0x0f0) >> 4;
1188 
1189  switch (recommended_op)
1190    {
1191    case XmDROP_MOVE:
1192      context->suggested_action = GDK_ACTION_MOVE;
1193      break;
1194    case XmDROP_COPY:
1195      context->suggested_action = GDK_ACTION_COPY;
1196      break;
1197    case XmDROP_LINK:
1198      context->suggested_action = GDK_ACTION_LINK;
1199      break;
1200    default:
1201      context->suggested_action = GDK_ACTION_COPY;
1202      break;
1203    }
1204
1205  context->actions = 0;
1206  if (possible_ops & XmDROP_MOVE)
1207    context->actions |= GDK_ACTION_MOVE;
1208  if (possible_ops & XmDROP_COPY)
1209    context->actions |= GDK_ACTION_COPY;
1210  if (possible_ops & XmDROP_LINK)
1211    context->actions |= GDK_ACTION_LINK;
1212}
1213
1214static guint16
1215motif_dnd_get_flags (GdkDragContext *context)
1216{
1217  guint16 flags = 0;
1218 
1219  switch (context->suggested_action)
1220    {
1221    case GDK_ACTION_MOVE:
1222      flags = XmDROP_MOVE;
1223      break;
1224    case GDK_ACTION_COPY:
1225      flags = XmDROP_COPY;
1226      break;
1227    case GDK_ACTION_LINK:
1228      flags = XmDROP_LINK;
1229      break;
1230    default:
1231      flags = XmDROP_NOOP;
1232      break;
1233    }
1234 
1235  if (context->actions & GDK_ACTION_MOVE)
1236    flags |= XmDROP_MOVE << 8;
1237  if (context->actions & GDK_ACTION_COPY)
1238    flags |= XmDROP_COPY << 8;
1239  if (context->actions & GDK_ACTION_LINK)
1240    flags |= XmDROP_LINK << 8;
1241
1242  return flags;
1243}
1244
1245/* Source Side */
1246
1247static void
1248motif_set_targets (GdkDragContext *context)
1249{
1250  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
1251  MotifDragInitiatorInfo info;
1252  gint i;
1253  static GdkAtom motif_drag_initiator_info = GDK_NONE;
1254
1255  if (!motif_drag_initiator_info)
1256    motif_drag_initiator_info = gdk_atom_intern ("_MOTIF_DRAG_INITIATOR_INFO", FALSE);
1257
1258  info.byte_order = local_byte_order;
1259  info.protocol_version = 0;
1260 
1261  info.targets_index = motif_add_to_target_table (context->targets);
1262
1263  for (i=0; ; i++)
1264    {
1265      gchar buf[20];
1266      g_snprintf(buf, 20, "_GDK_SELECTION_%d", i);
1267     
1268      private->motif_selection = gdk_atom_intern (buf, FALSE);
1269      if (!XGetSelectionOwner (gdk_display, private->motif_selection))
1270        break;
1271    }
1272
1273  info.selection_atom = private->motif_selection;
1274
1275  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
1276                   GDK_WINDOW_XWINDOW (context->source_window),
1277                   private->motif_selection,
1278                   motif_drag_initiator_info, 8, PropModeReplace,
1279                   (guchar *)&info, sizeof (info));
1280
1281  private->motif_targets_set = 1;
1282}
1283
1284guint32
1285motif_check_dest (Window win)
1286{
1287  gboolean retval = FALSE;
1288  MotifDragReceiverInfo *info;
1289  Atom type = None;
1290  int format;
1291  unsigned long nitems, after;
1292
1293  if (!motif_drag_receiver_info_atom)
1294    motif_drag_receiver_info_atom = gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE);
1295
1296  gdk_error_trap_push ();
1297  XGetWindowProperty (gdk_display, win,
1298                      motif_drag_receiver_info_atom,
1299                      0, (sizeof(*info)+3)/4, False, AnyPropertyType,
1300                      &type, &format, &nitems, &after,
1301                      (guchar **)&info);
1302
1303  if (gdk_error_trap_pop() == 0)
1304    {
1305      if (type != None)
1306        {
1307          if ((format == 8) && (nitems == sizeof(*info)))
1308            {
1309              if ((info->protocol_version == 0) &&
1310                  ((info->protocol_style == XmDRAG_PREFER_PREREGISTER) ||
1311                   (info->protocol_style == XmDRAG_PREFER_DYNAMIC) ||
1312                   (info->protocol_style == XmDRAG_DYNAMIC)))
1313                retval = TRUE;
1314            }
1315          else
1316            {
1317              GDK_NOTE (DND,
1318                        g_warning ("Invalid Motif drag receiver property on window %ld\n", win));
1319            }
1320         
1321          XFree (info);
1322        }
1323    }
1324
1325  return retval ? win : GDK_NONE;
1326 
1327}
1328
1329static void
1330motif_send_enter (GdkDragContext  *context,
1331                  guint32          time)
1332{
1333  XEvent xev;
1334  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
1335
1336  xev.xclient.type = ClientMessage;
1337  xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
1338  xev.xclient.format = 8;
1339  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
1340
1341  MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_ENTER;
1342  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1343  MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
1344  MOTIF_XCLIENT_LONG (&xev, 1) = time;
1345  MOTIF_XCLIENT_LONG (&xev, 2) = GDK_WINDOW_XWINDOW (context->source_window);
1346
1347  if (!private->motif_targets_set)
1348    motif_set_targets (context);
1349
1350  MOTIF_XCLIENT_LONG (&xev, 3) = private->motif_selection;
1351
1352  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
1353                        FALSE, 0, &xev))
1354    GDK_NOTE (DND,
1355              g_message ("Send event to %lx failed",
1356                         GDK_WINDOW_XWINDOW (context->dest_window)));
1357}
1358
1359static void
1360motif_send_leave (GdkDragContext  *context,
1361                  guint32          time)
1362{
1363  XEvent xev;
1364
1365  xev.xclient.type = ClientMessage;
1366  xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
1367  xev.xclient.format = 8;
1368  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
1369
1370  MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_LEAVE;
1371  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1372  MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
1373  MOTIF_XCLIENT_LONG (&xev, 1) = time;
1374  MOTIF_XCLIENT_LONG (&xev, 2) = GDK_WINDOW_XWINDOW (context->source_window);
1375  MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1376
1377  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
1378                        FALSE, 0, &xev))
1379    GDK_NOTE (DND,
1380              g_message ("Send event to %lx failed",
1381                         GDK_WINDOW_XWINDOW (context->dest_window)));
1382}
1383
1384static gboolean
1385motif_send_motion (GdkDragContext  *context,
1386                    gint            x_root,
1387                    gint            y_root,
1388                    GdkDragAction   action,
1389                    guint32         time)
1390{
1391  gboolean retval;
1392  XEvent xev;
1393  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
1394
1395  xev.xclient.type = ClientMessage;
1396  xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
1397  xev.xclient.format = 8;
1398  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
1399
1400  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1401  MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1402  MOTIF_XCLIENT_LONG (&xev, 1) = time;
1403
1404  if ((context->suggested_action != private->old_action) ||
1405      (context->actions != private->old_actions))
1406    {
1407      MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
1408
1409      /* private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
1410      retval = TRUE;
1411    }
1412  else
1413    {
1414      MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
1415
1416      MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
1417      MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
1418     
1419      private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1420      retval = FALSE;
1421    }
1422
1423  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
1424                        FALSE, 0, &xev))
1425    GDK_NOTE (DND,
1426              g_message ("Send event to %lx failed",
1427                         GDK_WINDOW_XWINDOW (context->dest_window)));
1428
1429  return retval;
1430}
1431
1432static void
1433motif_send_drop (GdkDragContext *context, guint32 time)
1434{
1435  XEvent xev;
1436  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
1437
1438  xev.xclient.type = ClientMessage;
1439  xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
1440  xev.xclient.format = 8;
1441  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
1442
1443  MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START;
1444  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1445  MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1446  MOTIF_XCLIENT_LONG (&xev, 1)  = time;
1447
1448  MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
1449  MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
1450
1451  MOTIF_XCLIENT_LONG (&xev, 3)  = private->motif_selection;
1452  MOTIF_XCLIENT_LONG (&xev, 4)  = GDK_WINDOW_XWINDOW (context->source_window);
1453
1454  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
1455                        FALSE, 0, &xev))
1456    GDK_NOTE (DND,
1457              g_message ("Send event to %lx failed",
1458                         GDK_WINDOW_XWINDOW (context->dest_window)));
1459}
1460
1461/* Target Side */
1462
1463static gboolean
1464motif_read_initiator_info (Window source_window,
1465                           Atom atom,
1466                           GList  **targets,
1467                           GdkAtom *selection)
1468{
1469  GList *tmp_list;
1470  static GdkAtom motif_drag_initiator_info = GDK_NONE;
1471  GdkAtom type;
1472  gint format;
1473  gulong nitems;
1474  gulong bytes_after;
1475
1476  MotifDragInitiatorInfo *initiator_info;
1477
1478  if (!motif_drag_initiator_info)
1479    motif_drag_initiator_info = gdk_atom_intern ("_MOTIF_DRAG_INITIATOR_INFO", FALSE);
1480
1481  gdk_error_trap_push ();
1482  XGetWindowProperty (gdk_display, source_window, atom,
1483                      0, sizeof(*initiator_info), FALSE,
1484                      motif_drag_initiator_info,
1485                      &type, &format, &nitems, &bytes_after,
1486                      (guchar **)&initiator_info);
1487
1488  if (gdk_error_trap_pop () || (format != 8) || (nitems != sizeof (MotifDragInitiatorInfo)) || (bytes_after != 0))
1489    {
1490      g_warning ("Error reading initiator info\n");
1491      return FALSE;
1492    }
1493
1494  motif_read_target_table ();
1495
1496  initiator_info->targets_index =
1497    card16_to_host (initiator_info->targets_index, initiator_info->byte_order);
1498  initiator_info->selection_atom =
1499    card32_to_host (initiator_info->selection_atom, initiator_info->byte_order);
1500 
1501  if (initiator_info->targets_index >= motif_n_target_lists)
1502    {
1503      g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
1504      XFree (initiator_info);
1505      return GDK_FILTER_REMOVE;
1506    }
1507
1508  tmp_list = g_list_last (motif_target_lists[initiator_info->targets_index]);
1509
1510  *targets = NULL;
1511  while (tmp_list)
1512    {
1513      *targets = g_list_prepend (*targets,
1514                                 tmp_list->data);
1515      tmp_list = tmp_list->prev;
1516    }
1517
1518#ifdef G_ENABLE_DEBUG
1519  if (gdk_debug_flags & GDK_DEBUG_DND)
1520    print_target_list (*targets);
1521#endif /* G_ENABLE_DEBUG */
1522
1523  *selection = initiator_info->selection_atom;
1524
1525  XFree (initiator_info);
1526
1527  return TRUE;
1528}
1529
1530static GdkDragContext *
1531motif_drag_context_new (GdkWindow *dest_window,
1532                        guint32    timestamp,
1533                        guint32    source_window,
1534                        guint32    atom)
1535{
1536  GdkDragContext *new_context;
1537  GdkDragContextPrivate *private;
1538
1539  /* FIXME, current_dest_drag really shouldn't be NULL'd
1540   * if we error below.
1541   */
1542  if (current_dest_drag != NULL)
1543    {
1544      if (timestamp >= current_dest_drag->start_time)
1545        {
1546          gdk_drag_context_unref (current_dest_drag);
1547          current_dest_drag = NULL;
1548        }
1549      else
1550        return NULL;
1551    }
1552
1553  new_context = gdk_drag_context_new ();
1554  private = (GdkDragContextPrivate *)new_context;
1555
1556  new_context->protocol = GDK_DRAG_PROTO_MOTIF;
1557  new_context->is_source = FALSE;
1558
1559  new_context->source_window = gdk_window_lookup (source_window);
1560  if (new_context->source_window)
1561    gdk_window_ref (new_context->source_window);
1562  else
1563    {
1564      new_context->source_window = gdk_window_foreign_new (source_window);
1565      if (!new_context->source_window)
1566        {
1567          gdk_drag_context_unref (new_context);
1568          return NULL;
1569        }
1570    }
1571
1572  new_context->dest_window = dest_window;
1573  gdk_window_ref (dest_window);
1574  new_context->start_time = timestamp;
1575
1576  if (!motif_read_initiator_info(source_window,
1577                                 atom,
1578                                 &new_context->targets,
1579                                 &private->motif_selection))
1580    {
1581      gdk_drag_context_unref (new_context);
1582      return NULL;
1583    }
1584
1585  return new_context;
1586}
1587
1588/*
1589 * The MOTIF drag protocol has no real provisions for distinguishing
1590 * multiple simultaneous drops. If the sources grab the pointer
1591 * when doing drags, that shouldn't happen, in any case. If it
1592 * does, we can't do much except hope for the best.
1593 */
1594
1595static GdkFilterReturn
1596motif_top_level_enter (GdkEvent *event,
1597                       guint16   flags,
1598                       guint32   timestamp,
1599                       guint32   source_window,
1600                       guint32   atom)
1601{
1602  GdkDragContext *new_context;
1603
1604  GDK_NOTE(DND, g_message ("Motif DND top level enter: flags: %#4x time: %d source_widow: %#4x atom: %d",
1605                           flags, timestamp, source_window, atom));
1606
1607  new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1608  if (!new_context)
1609    return GDK_FILTER_REMOVE;
1610
1611  event->dnd.type = GDK_DRAG_ENTER;
1612  event->dnd.context = new_context;
1613  gdk_drag_context_ref (new_context);
1614
1615  current_dest_drag = new_context;
1616
1617  return GDK_FILTER_TRANSLATE;
1618}
1619
1620GdkFilterReturn
1621motif_top_level_leave (GdkEvent *event,
1622                       guint16   flags,
1623                       guint32   timestamp)
1624{
1625  GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
1626                           flags, timestamp));
1627
1628  if ((current_dest_drag != NULL) &&
1629      (current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1630      (timestamp >= current_dest_drag->start_time))
1631    {
1632      event->dnd.type = GDK_DRAG_LEAVE;
1633      /* Pass ownership of context to the event */
1634      event->dnd.context = current_dest_drag;
1635
1636      current_dest_drag = NULL;
1637
1638      return GDK_FILTER_TRANSLATE;
1639    }
1640  else
1641    return GDK_FILTER_REMOVE;
1642}
1643
1644GdkFilterReturn
1645motif_motion (GdkEvent *event,
1646              guint16   flags,
1647              guint32   timestamp,
1648              gint16    x_root,
1649              gint16    y_root)
1650{
1651  GdkDragContextPrivate *private;
1652
1653  GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
1654                           flags, timestamp, x_root, y_root));
1655
1656  if ((current_dest_drag != NULL) &&
1657      (current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1658      (timestamp >= current_dest_drag->start_time))
1659    {
1660      private = (GdkDragContextPrivate *)current_dest_drag;
1661
1662      event->dnd.type = GDK_DRAG_MOTION;
1663      event->dnd.context = current_dest_drag;
1664      gdk_drag_context_ref (current_dest_drag);
1665
1666      event->dnd.time = timestamp;
1667
1668      motif_dnd_translate_flags (current_dest_drag, flags);
1669
1670      event->dnd.x_root = x_root;
1671      event->dnd.y_root = y_root;
1672
1673      private->last_x = x_root;
1674      private->last_y = y_root;
1675
1676      private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1677
1678      return GDK_FILTER_TRANSLATE;
1679    }
1680
1681  return GDK_FILTER_REMOVE;
1682}
1683
1684GdkFilterReturn
1685motif_operation_changed (GdkEvent *event,
1686                         guint16   flags,
1687                         guint32   timestamp)
1688{
1689  GdkDragContextPrivate *private;
1690
1691  GDK_NOTE(DND, g_message ("Motif DND operation changed: flags: %#4x time: %d",
1692                           flags, timestamp));
1693
1694  if ((current_dest_drag != NULL) &&
1695      (current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1696      (timestamp >= current_dest_drag->start_time))
1697    {
1698      event->dnd.type = GDK_DRAG_MOTION;
1699      event->dnd.send_event = FALSE;
1700      event->dnd.context = current_dest_drag;
1701      gdk_drag_context_ref (current_dest_drag);
1702
1703      event->dnd.time = timestamp;
1704      private = (GdkDragContextPrivate *)current_dest_drag;
1705
1706      motif_dnd_translate_flags (current_dest_drag, flags);
1707
1708      event->dnd.x_root = private->last_x;
1709      event->dnd.y_root = private->last_y;
1710
1711      private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
1712
1713      return GDK_FILTER_TRANSLATE;
1714    }
1715
1716  return GDK_FILTER_REMOVE;
1717}
1718
1719GdkFilterReturn
1720motif_drop_start (GdkEvent *event,
1721                  guint16   flags,
1722                  guint32   timestamp,
1723                  guint32   source_window,
1724                  guint32   atom,
1725                  gint16    x_root,
1726                  gint16    y_root)
1727{
1728  GdkDragContext *new_context;
1729
1730
1731  GDK_NOTE(DND, g_message ("Motif DND drop start: flags: %#4x time: %d (%d, %d) source_widow: %#4x atom: %d",
1732                           flags, timestamp, x_root, y_root, source_window, atom));
1733
1734  new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1735  if (!new_context)
1736    return GDK_FILTER_REMOVE;
1737
1738  motif_dnd_translate_flags (new_context, flags);
1739
1740  event->dnd.type = GDK_DROP_START;
1741  event->dnd.context = new_context;
1742  event->dnd.time = timestamp;
1743  event->dnd.x_root = x_root;
1744  event->dnd.y_root = y_root;
1745
1746  gdk_drag_context_ref (new_context);
1747  current_dest_drag = new_context;
1748
1749  return GDK_FILTER_TRANSLATE;
1750
1751
1752GdkFilterReturn
1753motif_drag_status (GdkEvent *event,
1754                   guint16   flags,
1755                   guint32   timestamp)
1756{
1757  GdkDragContext *context;
1758 
1759  GDK_NOTE (DND,
1760            g_message ("Motif status message: flags %x", flags));
1761
1762  context = gdk_drag_context_find (TRUE,
1763                                   GDK_WINDOW_XWINDOW (event->any.window),
1764                                   None);
1765
1766  if (context)
1767    {
1768      GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
1769      if ((private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
1770          (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
1771        private->drag_status = GDK_DRAG_STATUS_DRAG;
1772     
1773      event->dnd.type = GDK_DRAG_STATUS;
1774      event->dnd.send_event = FALSE;
1775      event->dnd.context = context;
1776      gdk_drag_context_ref (context);
1777
1778      event->dnd.time = timestamp;
1779
1780      if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
1781        {
1782          switch (flags & 0x000f)
1783            {
1784            case XmDROP_NOOP:
1785              context->action = 0;
1786              break;
1787            case XmDROP_MOVE:
1788                context->action = GDK_ACTION_MOVE;
1789                break;
1790            case XmDROP_COPY:
1791              context->action = GDK_ACTION_COPY;
1792              break;
1793            case XmDROP_LINK:
1794              context->action = GDK_ACTION_LINK;
1795              break;
1796            }
1797        }
1798      else
1799        context->action = 0;
1800
1801      return GDK_FILTER_TRANSLATE;
1802    }
1803  return GDK_FILTER_REMOVE;
1804}
1805
1806GdkFilterReturn
1807motif_dnd_filter (GdkXEvent *xev,
1808                  GdkEvent  *event,
1809                  gpointer data)
1810{
1811  XEvent *xevent = (XEvent *)xev;
1812
1813  guint8 reason;
1814  guint16 flags;
1815  guint32 timestamp;
1816  guint32 source_window;
1817  GdkAtom atom;
1818  gint16 x_root, y_root;
1819  gboolean is_reply;
1820 
1821  /* First read some fields common to all Motif DND messages */
1822
1823  reason = MOTIF_UNPACK_BYTE (xevent, 0);
1824  flags = MOTIF_UNPACK_SHORT (xevent, 1);
1825  timestamp = MOTIF_UNPACK_LONG (xevent, 1);
1826
1827  is_reply = ((reason & 0x80) != 0);
1828
1829  switch (reason & 0x7f)
1830    {
1831    case XmTOP_LEVEL_ENTER:
1832      source_window = MOTIF_UNPACK_LONG (xevent, 2);
1833      atom = MOTIF_UNPACK_LONG (xevent, 3);
1834      return motif_top_level_enter (event, flags, timestamp, source_window, atom);
1835    case XmTOP_LEVEL_LEAVE:
1836      return motif_top_level_leave (event, flags, timestamp);
1837
1838    case XmDRAG_MOTION:
1839      x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1840      y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1841     
1842      if (!is_reply)
1843        return motif_motion (event, flags, timestamp, x_root, y_root);
1844      else
1845        return motif_drag_status (event, flags, timestamp);
1846
1847    case XmDROP_SITE_ENTER:
1848      return motif_drag_status (event, flags, timestamp);
1849
1850    case XmDROP_SITE_LEAVE:
1851      return motif_drag_status (event,
1852                                XmNO_DROP_SITE << 8 | XmDROP_NOOP,
1853                                timestamp);
1854    case XmDROP_START:
1855      x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1856      y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1857      atom = MOTIF_UNPACK_LONG (xevent, 3);
1858      source_window = MOTIF_UNPACK_LONG (xevent, 4);
1859
1860      if (!is_reply)
1861        return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
1862     
1863     break;
1864    case XmOPERATION_CHANGED:
1865      if (!is_reply)
1866        return motif_operation_changed (event, flags, timestamp);
1867      else
1868        return motif_drag_status (event, flags, timestamp);
1869
1870      break;
1871      /* To the best of my knowledge, these next two messages are
1872       * not part of the protocol, though they are defined in
1873       * the header files.
1874       */
1875    case XmDROP_FINISH:
1876    case XmDRAG_DROP_FINISH:
1877      break;
1878    }
1879
1880  return GDK_FILTER_REMOVE;
1881}
1882
1883/*************************************************************
1884 ***************************** XDND **************************
1885 *************************************************************/
1886
1887/* Utility functions */
1888
1889static struct {
1890  gchar *name;
1891  GdkAtom atom;
1892  GdkDragAction action;
1893} xdnd_actions_table[] = {
1894    { "XdndActionCopy",    GDK_NONE, GDK_ACTION_COPY },
1895    { "XdndActionMove",    GDK_NONE, GDK_ACTION_MOVE },
1896    { "XdndActionLink",    GDK_NONE, GDK_ACTION_LINK },
1897    { "XdndActionAsk",     GDK_NONE, GDK_ACTION_ASK  },
1898    { "XdndActionPrivate", GDK_NONE, GDK_ACTION_COPY },
1899  };
1900
1901static const gint xdnd_n_actions = sizeof(xdnd_actions_table) / sizeof(xdnd_actions_table[0]);
1902static gboolean xdnd_actions_initialized = FALSE;
1903
1904static void
1905xdnd_initialize_actions (void)
1906{
1907  gint i;
1908 
1909  xdnd_actions_initialized = TRUE;
1910  for (i=0; i < xdnd_n_actions; i++)
1911    xdnd_actions_table[i].atom = gdk_atom_intern (xdnd_actions_table[i].name, FALSE);
1912}
1913
1914static GdkDragAction
1915xdnd_action_from_atom (GdkAtom atom)
1916{
1917  gint i;
1918
1919  if (!xdnd_actions_initialized)
1920    xdnd_initialize_actions();
1921
1922  for (i=0; i<xdnd_n_actions; i++)
1923    if (atom == xdnd_actions_table[i].atom)
1924      return xdnd_actions_table[i].action;
1925
1926  return 0;
1927}
1928
1929static GdkAtom
1930xdnd_action_to_atom (GdkDragAction action)
1931{
1932  gint i;
1933
1934  if (!xdnd_actions_initialized)
1935    xdnd_initialize_actions();
1936
1937  for (i=0; i<xdnd_n_actions; i++)
1938    if (action == xdnd_actions_table[i].action)
1939      return xdnd_actions_table[i].atom;
1940
1941  return GDK_NONE;
1942}
1943
1944static GdkAtom xdnd_aware_atom = GDK_NONE;
1945
1946/* Source side */
1947
1948static GdkFilterReturn
1949xdnd_status_filter (GdkXEvent *xev,
1950                    GdkEvent  *event,
1951                    gpointer   data)
1952{
1953  XEvent *xevent = (XEvent *)xev;
1954  guint32 dest_window = xevent->xclient.data.l[0];
1955  guint32 flags = xevent->xclient.data.l[1];
1956  GdkAtom action = xevent->xclient.data.l[4];
1957  GdkDragContext *context;
1958 
1959  GDK_NOTE (DND,
1960            g_message ("XdndStatus: dest_window: %#x  action: %ld",
1961                       dest_window, action));
1962
1963 
1964  context = gdk_drag_context_find (TRUE, xevent->xclient.window, dest_window);
1965  if (context)
1966    {
1967      GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
1968      if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
1969        private->drag_status = GDK_DRAG_STATUS_DRAG;
1970     
1971      event->dnd.send_event = FALSE;
1972      event->dnd.type = GDK_DRAG_STATUS;
1973      event->dnd.context = context;
1974      gdk_drag_context_ref (context);
1975
1976      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1977      if (!(action != 0) != !(flags & 1))
1978        {
1979          GDK_NOTE (DND,
1980                    g_warning ("Received status event with flags not corresponding to action!\n"));
1981          action = 0;
1982        }
1983
1984      context->action = xdnd_action_from_atom (action);
1985
1986      return GDK_FILTER_TRANSLATE;
1987    }
1988
1989  return GDK_FILTER_REMOVE;
1990}
1991
1992static GdkFilterReturn
1993xdnd_finished_filter (GdkXEvent *xev,
1994                      GdkEvent  *event,
1995                      gpointer   data)
1996{
1997  XEvent *xevent = (XEvent *)xev;
1998  guint32 dest_window = xevent->xclient.data.l[0];
1999  GdkDragContext *context;
2000 
2001  GDK_NOTE (DND,
2002            g_message ("XdndFinished: dest_window: %#x", dest_window));
2003
2004  context = gdk_drag_context_find (TRUE, xevent->xclient.window, dest_window);
2005  if (context)
2006    {
2007      event->dnd.type = GDK_DROP_FINISHED;
2008      event->dnd.context = context;
2009      gdk_drag_context_ref (context);
2010
2011      return GDK_FILTER_TRANSLATE;
2012    }
2013
2014  return GDK_FILTER_REMOVE;
2015}
2016
2017static void
2018xdnd_set_targets (GdkDragContext *context)
2019{
2020  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2021  GdkAtom *atomlist;
2022  GList *tmp_list = context->targets;
2023  gint i;
2024  gint n_atoms = g_list_length (context->targets);
2025
2026  atomlist = g_new (GdkAtom, n_atoms);
2027  i = 0;
2028  while (tmp_list)
2029    {
2030      atomlist[i] = GPOINTER_TO_INT (tmp_list->data);
2031      tmp_list = tmp_list->next;
2032      i++;
2033    }
2034
2035  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2036                   GDK_WINDOW_XWINDOW (context->source_window),
2037                   gdk_atom_intern ("XdndTypeList", FALSE),
2038                   XA_ATOM, 32, PropModeReplace,
2039                   (guchar *)atomlist, n_atoms);
2040
2041  g_free (atomlist);
2042
2043  private->xdnd_targets_set = 1;
2044}
2045
2046static void
2047xdnd_set_actions (GdkDragContext *context)
2048{
2049  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2050  GdkAtom *atomlist;
2051  gint i;
2052  gint n_atoms;
2053  guint actions;
2054
2055  if (!xdnd_actions_initialized)
2056    xdnd_initialize_actions();
2057 
2058  actions = context->actions;
2059  n_atoms = 0;
2060  for (i=0; i<xdnd_n_actions; i++)
2061    {
2062      if (actions & xdnd_actions_table[i].action)
2063        {
2064          actions &= ~xdnd_actions_table[i].action;
2065          n_atoms++;
2066        }
2067    }
2068
2069  atomlist = g_new (GdkAtom, n_atoms);
2070
2071  actions = context->actions;
2072  n_atoms = 0;
2073  for (i=0; i<xdnd_n_actions; i++)
2074    {
2075      if (actions & xdnd_actions_table[i].action)
2076        {
2077          actions &= ~xdnd_actions_table[i].action;
2078          atomlist[n_atoms] = xdnd_actions_table[i].atom;
2079          n_atoms++;
2080        }
2081    }
2082
2083  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2084                   GDK_WINDOW_XWINDOW (context->source_window),
2085                   gdk_atom_intern ("XdndActionList", FALSE),
2086                   XA_ATOM, 32, PropModeReplace,
2087                   (guchar *)atomlist, n_atoms);
2088
2089  g_free (atomlist);
2090
2091  private->xdnd_actions_set = 1;
2092  private->xdnd_actions = context->actions;
2093}
2094
2095/*************************************************************
2096 * xdnd_send_xevent:
2097 *     Like gdk_send_event, but if the target is the root
2098 *     window, sets an event mask of ButtonPressMask, otherwise
2099 *     an event mask of 0.
2100 *   arguments:
2101 *     
2102 *   results:
2103 *************************************************************/
2104
2105gint
2106xdnd_send_xevent (Window window, gboolean propagate,
2107                  XEvent *event_send)
2108{
2109  if (window == gdk_root_window)
2110    return gdk_send_xevent (window, propagate, ButtonPressMask, event_send);
2111  else
2112    return gdk_send_xevent (window, propagate, 0, event_send);
2113}
2114
2115static void
2116xdnd_send_enter (GdkDragContext *context)
2117{
2118  XEvent xev;
2119  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2120
2121  xev.xclient.type = ClientMessage;
2122  xev.xclient.message_type = gdk_atom_intern ("XdndEnter", FALSE);
2123  xev.xclient.format = 32;
2124  xev.xclient.window = private->drop_xid ?
2125                           private->drop_xid :
2126                           GDK_WINDOW_XWINDOW (context->dest_window);
2127  xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
2128  xev.xclient.data.l[1] = (3 << 24); /* version */
2129  xev.xclient.data.l[2] = 0;
2130  xev.xclient.data.l[3] = 0;
2131  xev.xclient.data.l[4] = 0;
2132
2133  if (!private->xdnd_selection)
2134    private->xdnd_selection = gdk_atom_intern ("XdndSelection", FALSE);
2135
2136  if (g_list_length (context->targets) > 3)
2137    {
2138      if (!private->xdnd_targets_set)
2139        xdnd_set_targets (context);
2140      xev.xclient.data.l[1] |= 1;
2141    }
2142  else
2143    {
2144      GList *tmp_list = context->targets;
2145      gint i = 2;
2146
2147      while (tmp_list)
2148        {
2149          xev.xclient.data.l[i] = GPOINTER_TO_INT (tmp_list->data);
2150          tmp_list = tmp_list->next;
2151          i++;
2152        }
2153    }
2154
2155  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
2156                         FALSE, &xev))
2157    {
2158      GDK_NOTE (DND,
2159                g_message ("Send event to %lx failed",
2160                           GDK_WINDOW_XWINDOW (context->dest_window)));
2161      gdk_window_unref (context->dest_window);
2162      context->dest_window = NULL;
2163    }
2164}
2165
2166static void
2167xdnd_send_leave (GdkDragContext *context)
2168{
2169  XEvent xev;
2170
2171  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2172
2173  xev.xclient.type = ClientMessage;
2174  xev.xclient.message_type = gdk_atom_intern ("XdndLeave", FALSE);
2175  xev.xclient.format = 32;
2176  xev.xclient.window = private->drop_xid ?
2177                           private->drop_xid :
2178                           GDK_WINDOW_XWINDOW (context->dest_window);
2179  xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
2180  xev.xclient.data.l[1] = 0;
2181  xev.xclient.data.l[2] = 0;
2182  xev.xclient.data.l[3] = 0;
2183  xev.xclient.data.l[4] = 0;
2184
2185  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
2186                         FALSE, &xev))
2187    {
2188      GDK_NOTE (DND,
2189                g_message ("Send event to %lx failed",
2190                           GDK_WINDOW_XWINDOW (context->dest_window)));
2191      gdk_window_unref (context->dest_window);
2192      context->dest_window = NULL;
2193    }
2194}
2195
2196static void
2197xdnd_send_drop (GdkDragContext *context, guint32 time)
2198{
2199  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2200  XEvent xev;
2201
2202  xev.xclient.type = ClientMessage;
2203  xev.xclient.message_type = gdk_atom_intern ("XdndDrop", FALSE);
2204  xev.xclient.format = 32;
2205  xev.xclient.window = private->drop_xid ?
2206                           private->drop_xid :
2207                           GDK_WINDOW_XWINDOW (context->dest_window);
2208  xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
2209  xev.xclient.data.l[1] = 0;
2210  xev.xclient.data.l[2] = time;
2211  xev.xclient.data.l[3] = 0;
2212  xev.xclient.data.l[4] = 0;
2213
2214  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
2215                         FALSE, &xev))
2216    {
2217      GDK_NOTE (DND,
2218                g_message ("Send event to %lx failed",
2219                           GDK_WINDOW_XWINDOW (context->dest_window)));
2220      gdk_window_unref (context->dest_window);
2221      context->dest_window = NULL;
2222    }
2223}
2224
2225static void
2226xdnd_send_motion (GdkDragContext *context,
2227                  gint            x_root,
2228                  gint            y_root,
2229                  GdkDragAction   action,
2230                  guint32         time)
2231{
2232  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2233  XEvent xev;
2234
2235  xev.xclient.type = ClientMessage;
2236  xev.xclient.message_type = gdk_atom_intern ("XdndPosition", FALSE);
2237  xev.xclient.format = 32;
2238  xev.xclient.window = private->drop_xid ?
2239                           private->drop_xid :
2240                           GDK_WINDOW_XWINDOW (context->dest_window);
2241  xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
2242  xev.xclient.data.l[1] = 0;
2243  xev.xclient.data.l[2] = (x_root << 16) | y_root;
2244  xev.xclient.data.l[3] = time;
2245  xev.xclient.data.l[4] = xdnd_action_to_atom (action);
2246
2247  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
2248                         FALSE, &xev))
2249    {
2250      GDK_NOTE (DND,
2251                g_message ("Send event to %lx failed",
2252                           GDK_WINDOW_XWINDOW (context->dest_window)));
2253      gdk_window_unref (context->dest_window);
2254      context->dest_window = NULL;
2255    }
2256  private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2257}
2258
2259static guint32
2260xdnd_check_dest (Window win)
2261{
2262  gboolean retval = FALSE;
2263  Atom type = None;
2264  int format;
2265  unsigned long nitems, after;
2266  GdkAtom *version;
2267  Window *proxy_data;
2268  Window proxy;
2269  static GdkAtom xdnd_proxy_atom = GDK_NONE;
2270
2271  gint old_warnings = gdk_error_warnings;
2272
2273  if (!xdnd_proxy_atom)
2274    xdnd_proxy_atom = gdk_atom_intern ("XdndProxy", FALSE);
2275
2276  if (!xdnd_aware_atom)
2277    xdnd_aware_atom = gdk_atom_intern ("XdndAware", FALSE);
2278
2279  proxy = GDK_NONE;
2280 
2281  gdk_error_code = 0;
2282  gdk_error_warnings = 0;
2283
2284  XGetWindowProperty (gdk_display, win,
2285                      xdnd_proxy_atom, 0,
2286                      1, False, AnyPropertyType,
2287                      &type, &format, &nitems, &after,
2288                      (guchar **)&proxy_data);
2289
2290  if (!gdk_error_code)
2291    {
2292      if (type != None)
2293        {
2294          if ((format == 32) && (nitems == 1))
2295            {
2296              proxy = *proxy_data;
2297            }
2298          else
2299            GDK_NOTE (DND,
2300                      g_warning ("Invalid XdndOwner property on window %ld\n", win));
2301         
2302          XFree (proxy_data);
2303        }
2304     
2305      XGetWindowProperty (gdk_display, proxy ? proxy : win,
2306                          xdnd_aware_atom, 0,
2307                          1, False, AnyPropertyType,
2308                          &type, &format, &nitems, &after,
2309                          (guchar **)&version);
2310     
2311      if (!gdk_error_code && type != None)
2312        {
2313          if ((format == 32) && (nitems == 1))
2314            {
2315              if (*version >= 3)
2316                retval = TRUE;
2317            }
2318          else
2319            GDK_NOTE (DND,
2320                      g_warning ("Invalid XdndAware property on window %ld\n", win));
2321         
2322          XFree (version);
2323        }
2324     
2325    }
2326
2327  gdk_error_warnings = old_warnings;
2328  gdk_error_code = 0;
2329 
2330  return retval ? (proxy ? proxy : win) : GDK_NONE;
2331}
2332
2333/* Target side */
2334
2335static void
2336xdnd_read_actions (GdkDragContext *context)
2337{
2338  Atom type;
2339  int format;
2340  gulong nitems, after;
2341  Atom *data;
2342
2343  gint i;
2344
2345  gint old_warnings = gdk_error_warnings;
2346
2347  gdk_error_code = 0;
2348  gdk_error_warnings = 0;
2349
2350  /* Get the XdndActionList, if set */
2351
2352  XGetWindowProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2353                      GDK_WINDOW_XWINDOW (context->source_window),
2354                      gdk_atom_intern ("XdndActionList", FALSE), 0, 65536,
2355                      False, XA_ATOM, &type, &format, &nitems,
2356                      &after, (guchar **)&data);
2357 
2358  if (!gdk_error_code && (format == 32) && (type == XA_ATOM))
2359    {
2360      context->actions = 0;
2361
2362      for (i=0; i<nitems; i++)
2363        context->actions |= xdnd_action_from_atom (data[i]);
2364
2365      ((GdkDragContextPrivate *)context)->xdnd_have_actions = TRUE;
2366
2367#ifdef G_ENABLE_DEBUG
2368      if (gdk_debug_flags & GDK_DEBUG_DND)
2369        {
2370          GString *action_str = g_string_new (NULL);
2371          if (context->actions & GDK_ACTION_MOVE)
2372            g_string_append(action_str, "MOVE ");
2373          if (context->actions & GDK_ACTION_COPY)
2374            g_string_append(action_str, "COPY ");
2375          if (context->actions & GDK_ACTION_LINK)
2376            g_string_append(action_str, "LINK ");
2377          if (context->actions & GDK_ACTION_ASK)
2378            g_string_append(action_str, "ASK ");
2379         
2380          g_message("Xdnd actions = %s", action_str->str);
2381          g_string_free (action_str, TRUE);
2382        }
2383#endif /* G_ENABLE_DEBUG */
2384
2385      XFree(data);
2386    }
2387
2388  gdk_error_warnings = old_warnings;
2389  gdk_error_code = 0;
2390}
2391
2392/* We have to make sure that the XdndActionList we keep internally
2393 * is up to date with the XdndActionList on the source window
2394 * because we get no notification, because Xdnd wasn't meant
2395 * to continually send actions. So we select on PropertyChangeMask
2396 * and add this filter.
2397 */
2398static GdkFilterReturn
2399xdnd_source_window_filter (GdkXEvent *xev,
2400                           GdkEvent  *event,
2401                           gpointer   cb_data)
2402{
2403  XEvent *xevent = (XEvent *)xev;
2404  GdkDragContext *context = cb_data;
2405
2406  if ((xevent->xany.type == PropertyNotify) &&
2407      (xevent->xproperty.atom == gdk_atom_intern ("XdndActionList", FALSE)))
2408    {
2409      xdnd_read_actions (context);
2410
2411      return GDK_FILTER_REMOVE;
2412    }
2413
2414  return GDK_FILTER_CONTINUE;
2415}
2416
2417static void
2418xdnd_manage_source_filter (GdkDragContext *context,
2419                           GdkWindow      *window,
2420                           gboolean        add_filter)
2421{
2422  gint old_warnings = 0;        /* quiet gcc */
2423  GdkWindowPrivate *private = (GdkWindowPrivate *)window;
2424                               
2425  gboolean is_foreign = (private->window_type == GDK_WINDOW_FOREIGN);
2426
2427  if (is_foreign)
2428    {
2429      old_warnings = gdk_error_warnings;
2430      gdk_error_warnings = 0;
2431    }
2432
2433  if (!private->destroyed)
2434    {
2435      if (add_filter)
2436        {
2437          gdk_window_set_events (window,
2438                                 gdk_window_get_events (window) |
2439                                 GDK_PROPERTY_CHANGE_MASK);
2440          gdk_window_add_filter (window, xdnd_source_window_filter, context);
2441
2442        }
2443      else
2444        {
2445          gdk_window_remove_filter (window,
2446                                    xdnd_source_window_filter,
2447                                    context);
2448          /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2449           * but we might want it for other reasons. (Like
2450           * INCR selection transactions).
2451           */
2452        }
2453    }
2454
2455  if (is_foreign)
2456    {
2457      gdk_flush();
2458      gdk_error_warnings = old_warnings;
2459    }
2460}
2461
2462static GdkFilterReturn
2463xdnd_enter_filter (GdkXEvent *xev,
2464                   GdkEvent  *event,
2465                   gpointer   cb_data)
2466{
2467  XEvent *xevent = (XEvent *)xev;
2468  GdkDragContext *new_context;
2469  gint i;
2470 
2471  Atom type;
2472  int format;
2473  gulong nitems, after;
2474  Atom *data;
2475
2476  guint32 source_window = xevent->xclient.data.l[0];
2477  gboolean get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2478  gint version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2479 
2480  GDK_NOTE (DND,
2481            g_message ("XdndEnter: source_window: %#x, version: %#x",
2482                       source_window, version));
2483
2484  if (version != 3)
2485    {
2486      /* Old source ignore */
2487      GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2488      return GDK_FILTER_REMOVE;
2489    }
2490 
2491  if (current_dest_drag != NULL)
2492    {
2493      gdk_drag_context_unref (current_dest_drag);
2494      current_dest_drag = NULL;
2495    }
2496
2497  new_context = gdk_drag_context_new ();
2498  new_context->protocol = GDK_DRAG_PROTO_XDND;
2499  new_context->is_source = FALSE;
2500
2501  new_context->source_window = gdk_window_lookup (source_window);
2502  if (new_context->source_window)
2503    gdk_window_ref (new_context->source_window);
2504  else
2505    {
2506      new_context->source_window = gdk_window_foreign_new (source_window);
2507      if (!new_context->source_window)
2508        {
2509          gdk_drag_context_unref (new_context);
2510          return GDK_FILTER_REMOVE;
2511        }
2512    }
2513  new_context->dest_window = event->any.window;
2514  gdk_window_ref (new_context->dest_window);
2515
2516  new_context->targets = NULL;
2517  if (get_types)
2518    {
2519      gdk_error_trap_push ();
2520      XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window),
2521                          source_window,
2522                          gdk_atom_intern ("XdndTypeList", FALSE), 0, 65536,
2523                          False, XA_ATOM, &type, &format, &nitems,
2524                          &after, (guchar **)&data);
2525
2526      if (gdk_error_trap_pop () || (format != 32) || (type != XA_ATOM))
2527        {
2528          gdk_drag_context_unref (new_context);
2529          return GDK_FILTER_REMOVE;
2530        }
2531
2532      for (i=0; i<nitems; i++)
2533        new_context->targets = g_list_append (new_context->targets,
2534                                              GUINT_TO_POINTER (data[i]));
2535
2536      XFree(data);
2537    }
2538  else
2539    {
2540      for (i=0; i<3; i++)
2541        if (xevent->xclient.data.l[2+i])
2542          new_context->targets = g_list_append (new_context->targets,
2543                                                GUINT_TO_POINTER (xevent->xclient.data.l[2+i]));
2544    }
2545
2546#ifdef G_ENABLE_DEBUG
2547  if (gdk_debug_flags & GDK_DEBUG_DND)
2548    print_target_list (new_context->targets);
2549#endif /* G_ENABLE_DEBUG */
2550
2551  xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
2552  xdnd_read_actions (new_context);
2553
2554  event->dnd.type = GDK_DRAG_ENTER;
2555  event->dnd.context = new_context;
2556  gdk_drag_context_ref (new_context);
2557
2558  current_dest_drag = new_context;
2559  ((GdkDragContextPrivate *)new_context)->xdnd_selection =
2560    gdk_atom_intern ("XdndSelection", FALSE);
2561
2562  return GDK_FILTER_TRANSLATE;
2563}
2564
2565static GdkFilterReturn
2566xdnd_leave_filter (GdkXEvent *xev,
2567                   GdkEvent  *event,
2568                   gpointer   data)
2569{
2570  XEvent *xevent = (XEvent *)xev;
2571  guint32 source_window = xevent->xclient.data.l[0];
2572 
2573  GDK_NOTE (DND,
2574            g_message ("XdndLeave: source_window: %#x",
2575                       source_window));
2576
2577  if ((current_dest_drag != NULL) &&
2578      (current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2579      (GDK_WINDOW_XWINDOW (current_dest_drag->source_window) == source_window))
2580    {
2581      event->dnd.type = GDK_DRAG_LEAVE;
2582      /* Pass ownership of context to the event */
2583      event->dnd.context = current_dest_drag;
2584
2585      current_dest_drag = NULL;
2586
2587      return GDK_FILTER_TRANSLATE;
2588    }
2589  else
2590    return GDK_FILTER_REMOVE;
2591}
2592
2593static GdkFilterReturn
2594xdnd_position_filter (GdkXEvent *xev,
2595                      GdkEvent  *event,
2596                      gpointer   data)
2597{
2598  XEvent *xevent = (XEvent *)xev;
2599  guint32 source_window = xevent->xclient.data.l[0];
2600  gint16 x_root = xevent->xclient.data.l[2] >> 16;
2601  gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
2602  guint32 time = xevent->xclient.data.l[3];
2603  GdkAtom action = xevent->xclient.data.l[4];
2604 
2605  GDK_NOTE (DND,
2606            g_message ("XdndPosition: source_window: %#x  position: (%d, %d)  time: %d  action: %ld",
2607                       source_window, x_root, y_root, time, action));
2608
2609 
2610  if ((current_dest_drag != NULL) &&
2611      (current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2612      (GDK_WINDOW_XWINDOW (current_dest_drag->source_window) == source_window))
2613    {
2614      event->dnd.type = GDK_DRAG_MOTION;
2615      event->dnd.context = current_dest_drag;
2616      gdk_drag_context_ref (current_dest_drag);
2617
2618      event->dnd.time = time;
2619
2620      current_dest_drag->suggested_action = xdnd_action_from_atom (action);
2621      if (!((GdkDragContextPrivate *)current_dest_drag)->xdnd_have_actions)
2622        current_dest_drag->actions = current_dest_drag->suggested_action;
2623
2624      event->dnd.x_root = x_root;
2625      event->dnd.y_root = y_root;
2626
2627      ((GdkDragContextPrivate *)current_dest_drag)->last_x = x_root;
2628      ((GdkDragContextPrivate *)current_dest_drag)->last_y = y_root;
2629     
2630      return GDK_FILTER_TRANSLATE;
2631    }
2632
2633  return GDK_FILTER_REMOVE;
2634}
2635
2636static GdkFilterReturn
2637xdnd_drop_filter (GdkXEvent *xev,
2638                  GdkEvent  *event,
2639                  gpointer   data)
2640{
2641  XEvent *xevent = (XEvent *)xev;
2642  guint32 source_window = xevent->xclient.data.l[0];
2643  guint32 time = xevent->xclient.data.l[2];
2644 
2645  GDK_NOTE (DND,
2646            g_message ("XdndDrop: source_window: %#x  time: %d",
2647                       source_window, time));
2648
2649  if ((current_dest_drag != NULL) &&
2650      (current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2651      (GDK_WINDOW_XWINDOW (current_dest_drag->source_window) == source_window))
2652    {
2653      GdkDragContextPrivate *private;
2654      private = (GdkDragContextPrivate *)current_dest_drag;
2655
2656      event->dnd.type = GDK_DROP_START;
2657
2658      event->dnd.context = current_dest_drag;
2659      gdk_drag_context_ref (current_dest_drag);
2660
2661      event->dnd.time = time;
2662      event->dnd.x_root = private->last_x;
2663      event->dnd.y_root = private->last_y;
2664     
2665      return GDK_FILTER_TRANSLATE;
2666    }
2667
2668  return GDK_FILTER_REMOVE;
2669}
2670
2671/*************************************************************
2672 ************************** Public API ***********************
2673 *************************************************************/
2674
2675void
2676gdk_dnd_init (void)
2677{
2678  init_byte_order();
2679
2680  gdk_add_client_message_filter (
2681        gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE),
2682        motif_dnd_filter, NULL);
2683  gdk_add_client_message_filter (
2684        gdk_atom_intern ("XdndEnter", FALSE),
2685        xdnd_enter_filter, NULL);
2686  gdk_add_client_message_filter (
2687        gdk_atom_intern ("XdndLeave", FALSE),
2688        xdnd_leave_filter, NULL);
2689  gdk_add_client_message_filter (
2690        gdk_atom_intern ("XdndPosition", FALSE),
2691        xdnd_position_filter, NULL);
2692  gdk_add_client_message_filter (
2693        gdk_atom_intern ("XdndStatus", FALSE),
2694        xdnd_status_filter, NULL);
2695  gdk_add_client_message_filter (
2696        gdk_atom_intern ("XdndFinished", FALSE),
2697        xdnd_finished_filter, NULL);
2698  gdk_add_client_message_filter (
2699        gdk_atom_intern ("XdndDrop", FALSE),
2700        xdnd_drop_filter, NULL);
2701}                     
2702
2703/* Source side */
2704
2705static void
2706gdk_drag_do_leave (GdkDragContext *context, guint32 time)
2707{
2708  if (context->dest_window)
2709    {
2710      switch (context->protocol)
2711        {
2712        case GDK_DRAG_PROTO_MOTIF:
2713          motif_send_leave (context, time);
2714          break;
2715        case GDK_DRAG_PROTO_XDND:
2716          xdnd_send_leave (context);
2717          break;
2718        case GDK_DRAG_PROTO_ROOTWIN:
2719        case GDK_DRAG_PROTO_NONE:
2720          break;
2721        }
2722
2723      gdk_window_unref (context->dest_window);
2724      context->dest_window = NULL;
2725    }
2726}
2727
2728GdkDragContext *
2729gdk_drag_begin (GdkWindow     *window,
2730                GList         *targets)
2731{
2732  GList *tmp_list;
2733  GdkDragContext *new_context;
2734 
2735  g_return_val_if_fail (window != NULL, NULL);
2736
2737  new_context = gdk_drag_context_new ();
2738  new_context->is_source = TRUE;
2739  new_context->source_window = window;
2740  gdk_window_ref (window);
2741
2742  tmp_list = g_list_last (targets);
2743  new_context->targets = NULL;
2744  while (tmp_list)
2745    {
2746      new_context->targets = g_list_prepend (new_context->targets,
2747                                             tmp_list->data);
2748      tmp_list = tmp_list->prev;
2749    }
2750
2751  new_context->actions = 0;
2752
2753  return new_context;
2754}
2755
2756guint32
2757gdk_drag_get_protocol (guint32          xid,
2758                       GdkDragProtocol *protocol)
2759{
2760  guint32 retval;
2761 
2762  if ((retval = xdnd_check_dest (xid)))
2763    {
2764      *protocol = GDK_DRAG_PROTO_XDND;
2765      GDK_NOTE (DND, g_message ("Entering dnd window %#x\n", xid));
2766      return retval;
2767    }
2768  else if ((retval = motif_check_dest (xid)))
2769    {
2770      *protocol = GDK_DRAG_PROTO_MOTIF;
2771      GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
2772      return retval;
2773    }
2774  else
2775    {
2776      /* Check if this is a root window */
2777
2778      gboolean rootwin = FALSE;
2779      gint old_warnings = gdk_error_warnings;
2780      Atom type = None;
2781      int format;
2782      unsigned long nitems, after;
2783      unsigned char *data;
2784
2785      if (xid == gdk_root_window)
2786        rootwin = TRUE;
2787
2788      gdk_error_warnings = 0;
2789     
2790      if (!rootwin)
2791        {
2792          gdk_error_code = 0;
2793
2794          XGetWindowProperty (gdk_display, xid,
2795                              gdk_atom_intern ("ENLIGHTENMENT_DESKTOP", FALSE),
2796                              0, 0, False, AnyPropertyType,
2797                              &type, &format, &nitems, &after, &data);
2798          if ((gdk_error_code == 0) && type != None)
2799            {
2800              XFree (data);
2801              rootwin = TRUE;
2802            }
2803        }
2804
2805      /* Until I find out what window manager the next one is for,
2806       * I'm leaving it commented out. It's supported in the
2807       * xscreensaver sources, though.
2808       */
2809#if 0
2810      if (!rootwin)
2811        {
2812          gdk_error_code = 0;
2813         
2814          XGetWindowProperty (gdk_display, win,
2815                              gdk_atom_intern ("__SWM_VROOT", FALSE),
2816                              0, 0, False, AnyPropertyType,
2817                              &type, &format, &nitems, &data);
2818          if ((gdk_error_code == 0) && type != None)
2819            rootwin = TRUE;
2820        }
2821#endif     
2822
2823      gdk_error_warnings = old_warnings;
2824
2825      if (rootwin)
2826        {
2827          *protocol = GDK_DRAG_PROTO_ROOTWIN;
2828          return xid;
2829        }
2830    }
2831
2832  *protocol = GDK_DRAG_PROTO_NONE;
2833  return GDK_NONE;
2834}
2835
2836void
2837gdk_drag_find_window (GdkDragContext  *context,
2838                      GdkWindow       *drag_window,
2839                      gint             x_root,
2840                      gint             y_root,
2841                      GdkWindow      **dest_window,
2842                      GdkDragProtocol *protocol)
2843{
2844  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2845  Window dest;
2846
2847  g_return_if_fail (context != NULL);
2848
2849  if (!private->window_cache)
2850    private->window_cache = gdk_window_cache_new();
2851
2852  dest = get_client_window_at_coords (private->window_cache,
2853                                      drag_window ?
2854                                      GDK_WINDOW_XWINDOW (drag_window) : GDK_NONE,
2855                                      x_root, y_root);
2856
2857  if (private->dest_xid != dest)
2858    {
2859      Window recipient;
2860      private->dest_xid = dest;
2861
2862      /* Check if new destination accepts drags, and which protocol */
2863
2864      /* There is some ugliness here. We actually need to pass
2865       * _three_ pieces of information to drag_motion - dest_window,
2866       * protocol, and the XID of the unproxied window. The first
2867       * two are passed explicitely, the third implicitly through
2868       * protocol->dest_xid.
2869       */
2870      if ((recipient = gdk_drag_get_protocol (dest, protocol)))
2871        {
2872          *dest_window = gdk_window_lookup (recipient);
2873          if (*dest_window)
2874            gdk_window_ref (*dest_window);
2875          else
2876            *dest_window = gdk_window_foreign_new (recipient);
2877        }
2878      else
2879        *dest_window = NULL;
2880    }
2881  else
2882    {
2883      *dest_window = context->dest_window;
2884      if (*dest_window)
2885        gdk_window_ref (*dest_window);
2886      *protocol = context->protocol;
2887    }
2888}
2889
2890gboolean       
2891gdk_drag_motion (GdkDragContext *context,
2892                 GdkWindow      *dest_window,
2893                 GdkDragProtocol protocol,
2894                 gint            x_root,
2895                 gint            y_root,
2896                 GdkDragAction   suggested_action,
2897                 GdkDragAction   possible_actions,
2898                 guint32         time)
2899{
2900  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2901
2902  g_return_val_if_fail (context != NULL, FALSE);
2903
2904  /* When we have a Xdnd target, make sure our XdndActionList
2905   * matches the current actions;
2906   */
2907  private->old_actions = context->actions;
2908  context->actions = possible_actions;
2909 
2910  if ((protocol == GDK_DRAG_PROTO_XDND) &&
2911      (!private->xdnd_actions_set ||
2912       private->xdnd_actions != possible_actions))
2913    xdnd_set_actions (context);
2914
2915  if (context->dest_window != dest_window)
2916    {
2917      GdkEvent temp_event;
2918
2919      /* Send a leave to the last destination */
2920      gdk_drag_do_leave (context, time);
2921      private->drag_status = GDK_DRAG_STATUS_DRAG;
2922
2923      /* Check if new destination accepts drags, and which protocol */
2924
2925      if (dest_window)
2926        {
2927          context->dest_window = dest_window;
2928          private->drop_xid = private->dest_xid;
2929          gdk_window_ref (context->dest_window);
2930          context->protocol = protocol;
2931
2932          switch (protocol)
2933            {
2934            case GDK_DRAG_PROTO_MOTIF:
2935              motif_send_enter (context, time);
2936              break;
2937
2938            case GDK_DRAG_PROTO_XDND:
2939              xdnd_send_enter (context);
2940              break;
2941
2942            case GDK_DRAG_PROTO_ROOTWIN:
2943            case GDK_DRAG_PROTO_NONE:
2944              break;
2945            }
2946          private->old_action = suggested_action;
2947          context->suggested_action = suggested_action;
2948          private->old_actions = possible_actions;
2949        }
2950      else
2951        {
2952          context->dest_window = NULL;
2953          private->drop_xid = None;
2954          context->action = 0;
2955        }
2956
2957      /* Push a status event, to let the client know that
2958       * the drag changed
2959       */
2960
2961      temp_event.dnd.type = GDK_DRAG_STATUS;
2962      temp_event.dnd.window = context->source_window;
2963      /* We use this to signal a synthetic status. Perhaps
2964       * we should use an extra field...
2965       */
2966      temp_event.dnd.send_event = TRUE;
2967
2968      temp_event.dnd.context = context;
2969      temp_event.dnd.time = time;
2970
2971      gdk_event_put (&temp_event);
2972    }
2973  else
2974    {
2975      private->old_action = context->suggested_action;
2976      context->suggested_action = suggested_action;
2977    }
2978
2979  /* Send a drag-motion event */
2980
2981  private->last_x = x_root;
2982  private->last_y = y_root;
2983     
2984  if (context->dest_window)
2985    {
2986      if (private->drag_status == GDK_DRAG_STATUS_DRAG)
2987        {
2988          switch (context->protocol)
2989            {
2990            case GDK_DRAG_PROTO_MOTIF:
2991              motif_send_motion (context, x_root, y_root, suggested_action, time);
2992              break;
2993             
2994            case GDK_DRAG_PROTO_XDND:
2995              xdnd_send_motion (context, x_root, y_root, suggested_action, time);
2996              break;
2997
2998            case GDK_DRAG_PROTO_ROOTWIN:
2999              {
3000                GdkEvent temp_event;
3001
3002                if (g_list_find (context->targets,
3003                                 GUINT_TO_POINTER (gdk_atom_intern ("application/x-rootwin-drop", FALSE))))
3004                  context->action = context->suggested_action;
3005                else
3006                  context->action = 0;
3007
3008                temp_event.dnd.type = GDK_DRAG_STATUS;
3009                temp_event.dnd.window = context->source_window;
3010                temp_event.dnd.send_event = FALSE;
3011                temp_event.dnd.context = context;
3012                temp_event.dnd.time = time;
3013
3014                gdk_event_put (&temp_event);
3015              }
3016              break;
3017            case GDK_DRAG_PROTO_NONE:
3018              g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3019              break;
3020            }
3021        }
3022      else
3023        return TRUE;
3024    }
3025
3026  return FALSE;
3027}
3028
3029void
3030gdk_drag_drop (GdkDragContext *context,
3031               guint32         time)
3032{
3033  g_return_if_fail (context != NULL);
3034
3035  if (context->dest_window)
3036    {
3037      switch (context->protocol)
3038        {
3039        case GDK_DRAG_PROTO_MOTIF:
3040          motif_send_leave (context, time);
3041          motif_send_drop (context, time);
3042          break;
3043         
3044        case GDK_DRAG_PROTO_XDND:
3045          xdnd_send_drop (context, time);
3046          break;
3047
3048        case GDK_DRAG_PROTO_ROOTWIN:
3049          g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3050          break;
3051        case GDK_DRAG_PROTO_NONE:
3052          g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3053          break;
3054        }
3055    }
3056}
3057
3058void
3059gdk_drag_abort (GdkDragContext *context,
3060                guint32         time)
3061{
3062  g_return_if_fail (context != NULL);
3063
3064  gdk_drag_do_leave (context, time);
3065}
3066
3067/* Destination side */
3068
3069void             
3070gdk_drag_status (GdkDragContext   *context,
3071                 GdkDragAction     action,
3072                 guint32           time)
3073{
3074  GdkDragContextPrivate *private;
3075  XEvent xev;
3076
3077  g_return_if_fail (context != NULL);
3078
3079  private = (GdkDragContextPrivate *)context;
3080
3081  context->action = action;
3082 
3083  if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3084    {
3085      xev.xclient.type = ClientMessage;
3086      xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
3087      xev.xclient.format = 8;
3088      xev.xclient.window = GDK_WINDOW_XWINDOW (context->source_window);
3089
3090      if (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3091        {
3092          MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3093        }
3094      else
3095        {
3096          if ((action != 0) != (private->old_action != 0))
3097            {
3098              if (action != 0)
3099                MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3100              else
3101                MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3102            }
3103          else
3104            MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3105        }
3106
3107      MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3108
3109      switch (action)
3110        {
3111        case GDK_ACTION_MOVE:
3112          MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3113          break;
3114        case GDK_ACTION_COPY:
3115          MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3116          break;
3117        case GDK_ACTION_LINK:
3118          MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3119          break;
3120        default:
3121          MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3122          break;
3123        }
3124
3125      if (action)
3126        MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3127      else
3128        MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3129
3130      MOTIF_XCLIENT_LONG (&xev, 1) = time;
3131      MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
3132      MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
3133
3134      if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
3135                       FALSE, 0, &xev))
3136        GDK_NOTE (DND,
3137                  g_message ("Send event to %lx failed",
3138                             GDK_WINDOW_XWINDOW (context->source_window)));
3139    }
3140  else if (context->protocol == GDK_DRAG_PROTO_XDND)
3141    {
3142      xev.xclient.type = ClientMessage;
3143      xev.xclient.message_type = gdk_atom_intern ("XdndStatus", FALSE);
3144      xev.xclient.format = 32;
3145      xev.xclient.window = GDK_WINDOW_XWINDOW (context->source_window);
3146
3147      xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->dest_window);
3148      xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
3149      xev.xclient.data.l[2] = 0;
3150      xev.xclient.data.l[3] = 0;
3151      xev.xclient.data.l[4] = xdnd_action_to_atom (action);
3152
3153      if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
3154                             FALSE, &xev))
3155        GDK_NOTE (DND,
3156                  g_message ("Send event to %lx failed",
3157                             GDK_WINDOW_XWINDOW (context->source_window)));
3158    }
3159
3160  private->old_action = action;
3161}
3162
3163void
3164gdk_drop_reply (GdkDragContext   *context,
3165                gboolean          ok,
3166                guint32           time)
3167{
3168  GdkDragContextPrivate *private;
3169
3170  g_return_if_fail (context != NULL);
3171
3172  private = (GdkDragContextPrivate *)context;
3173 
3174  if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3175    {
3176      XEvent xev;
3177
3178      xev.xclient.type = ClientMessage;
3179      xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
3180      xev.xclient.format = 8;
3181
3182      MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3183      MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3184      if (ok)
3185        MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY |
3186                                       (XmDROP_SITE_VALID << 4) |
3187                                       (XmDROP_NOOP << 8) |
3188                                       (XmDROP << 12);
3189      else
3190        MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP |
3191                                       (XmNO_DROP_SITE << 4) |
3192                                       (XmDROP_NOOP << 8) |
3193                                       (XmDROP_CANCEL << 12);
3194      MOTIF_XCLIENT_SHORT (&xev, 2) = private->last_x;
3195      MOTIF_XCLIENT_SHORT (&xev, 3) = private->last_y;
3196     
3197      gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
3198                       FALSE, 0, &xev);
3199    }
3200}
3201
3202void             
3203gdk_drop_finish (GdkDragContext   *context,
3204                 gboolean          success,
3205                 guint32           time)
3206{
3207  g_return_if_fail (context != NULL);
3208
3209  if (context->protocol == GDK_DRAG_PROTO_XDND)
3210    {
3211      XEvent xev;
3212
3213      xev.xclient.type = ClientMessage;
3214      xev.xclient.message_type = gdk_atom_intern ("XdndFinished", FALSE);
3215      xev.xclient.format = 32;
3216      xev.xclient.window = GDK_WINDOW_XWINDOW (context->source_window);
3217
3218      xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->dest_window);
3219      xev.xclient.data.l[1] = 0;
3220      xev.xclient.data.l[2] = 0;
3221      xev.xclient.data.l[3] = 0;
3222      xev.xclient.data.l[4] = 0;
3223
3224      if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
3225                             FALSE, &xev))
3226        GDK_NOTE (DND,
3227                  g_message ("Send event to %lx failed",
3228                             GDK_WINDOW_XWINDOW (context->source_window)));
3229    }
3230}
3231
3232
3233void           
3234gdk_window_register_dnd (GdkWindow      *window)
3235{
3236  static gulong xdnd_version = 3;
3237  MotifDragReceiverInfo info;
3238
3239  g_return_if_fail (window != NULL);
3240
3241  /* Set Motif drag receiver information property */
3242
3243  if (!motif_drag_receiver_info_atom)
3244    motif_drag_receiver_info_atom = gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE);
3245
3246  info.byte_order = local_byte_order;
3247  info.protocol_version = 0;
3248  info.protocol_style = XmDRAG_DYNAMIC;
3249  info.proxy_window = GDK_NONE;
3250  info.num_drop_sites = 0;
3251  info.total_size = sizeof(info);
3252
3253  XChangeProperty (gdk_display, GDK_WINDOW_XWINDOW (window),
3254                   motif_drag_receiver_info_atom,
3255                   motif_drag_receiver_info_atom,
3256                   8, PropModeReplace,
3257                   (guchar *)&info,
3258                   sizeof (info));
3259
3260  /* Set XdndAware */
3261
3262  if (!xdnd_aware_atom)
3263    xdnd_aware_atom = gdk_atom_intern ("XdndAware", FALSE);
3264
3265  /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
3266  XChangeProperty (GDK_WINDOW_XDISPLAY (window),
3267                   GDK_WINDOW_XWINDOW (window),
3268                   xdnd_aware_atom, XA_ATOM,
3269                   32, PropModeReplace,
3270                   (guchar *)&xdnd_version, 1);
3271}
3272
3273/*************************************************************
3274 * gdk_drag_get_selection:
3275 *     Returns the selection atom for the current source window
3276 *   arguments:
3277 *     
3278 *   results:
3279 *************************************************************/
3280
3281GdkAtom       
3282gdk_drag_get_selection (GdkDragContext *context)
3283{
3284  g_return_val_if_fail (context != NULL, GDK_NONE);
3285
3286  if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3287    return ((GdkDragContextPrivate *)context)->motif_selection;
3288  else if (context->protocol == GDK_DRAG_PROTO_XDND)
3289    return ((GdkDragContextPrivate *)context)->xdnd_selection;
3290  else
3291    return GDK_NONE;
3292}
3293
Note: See TracBrowser for help on using the repository browser.