Spelling fixes
[oweals/cde.git] / cde / programs / dsdm / proxy.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: proxy.c /main/6 1998/08/03 09:33:32 mgreess $ */
24 /*      Copyright (c) 1990, 1991, 1992, 1993 UNIX System Laboratories, Inc.     */
25 /*      Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 AT&T     */
26 /*        All Rights Reserved   */
27
28 /*      THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF          */
29 /*      UNIX System Laboratories, Inc.                          */
30 /*      The copyright notice above does not evidence any        */
31 /*      actual or intended publication of such source code.     */
32
33 /* 
34  * The Proxy Agent is used to translate Drag And Drop (dnd)
35  * messages between Motif and OLIT clients.
36  *
37  * LIMITATIONS:
38  * Any Motif or OLIT client running before the proxy was started will have to
39  * be restarted to participate in drag and drop.
40  */
41
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <X11/Xproto.h>
46 #include <X11/Xlib.h>
47 #include <X11/Xutil.h>
48 #include <X11/Xatom.h>
49
50 #ifdef    DEBUG
51 #define DPRINTF(args) (void) printf args
52 #define a2s(atom)       (atom ? XGetAtomName(dpy, atom) : "0")
53 #else  /* DEBUG */
54 #define DPRINTF(a)
55 #endif /* DEBUG */
56
57 #define OLIT_DROP_SELECTION     0
58 #define OLIT_DROP_TIME          1
59 #define OLIT_DROP_COORDINATE    2
60 #define OLIT_DROP_SITE_ID       3
61 #define OLIT_DROP_OPERATION     4
62
63 #define GET_PROPERTY_MAX        1000000
64 #define MOTIF_DRAG_PROTOCOL_VERSION 0
65 #define DROP_TABLE_MAX  10
66 #define MOTIF_MESSAGE_TYPE_MASK 0x7f
67 #define MOTIF_RECEIVER_BIT      0x80
68 #define MOTIF_DROP_SITE_STATUS_MASK ((CARD16) 0xFF0F)
69 #define MOTIF_DROP_SITE_STATUS_VALID ((CARD16) 0x0030)
70
71 enum {
72     MOTIF_TOP_LEVEL_ENTER,
73     MOTIF_TOP_LEVEL_LEAVE,
74     MOTIF_DRAG_MOTION,
75     MOTIF_DROP_SITE_ENTER,
76     MOTIF_DROP_SITE_LEAVE,
77     MOTIF_DROP_START,
78     MOTIF_DROP_FINISH,
79     MOTIF_DRAG_DROP_FINISH,
80     MOTIF_OPERATION_CHANGED
81 };
82
83 enum {
84         XmDRAG_NONE,
85         XmDRAG_DROP_ONLY,
86         XmDRAG_PREFER_PREREGISTER,
87         XmDRAG_PREREGISTER,
88         XmDRAG_PREFER_DYNAMIC,
89         XmDRAG_DYNAMIC,
90         XmDRAG_PREFER_RECEIVER
91 };
92
93 typedef struct {
94     Atom        source_handle;
95     Atom        proxy_handle;
96     Time        time_stamp;             /* time of drop_start or trigger */
97     Window      source_win;
98     Window      proxy_sel_req_win;      /* needed for concurrent drops */
99     Window      receiver_win;           /* was motif_receiver_win!!!
100                                          * This field was only for the olit
101                                          * source and was initialized in
102                                          * HandleOlitTrigger().
103                                          *
104                                          * Now this field is also served as
105                                          * the olit destination for the
106                                          * SUN_DRAGDROP_DONE situation and
107                                          * this field is initialized in
108                                          * ProxyMain:SelectionRequest... */
109     CARD32      motif_action;           /* for olit source only */
110     INT16       trigger_x;              /* for olit source only */
111     INT16       trigger_y;              /* for olit source only */
112     Atom        status;                 /* for olit source only!
113                                          * ATOM_MOTIF_SUCESS or
114                                          * ATOM_MOTIF_FAILURE.
115                                          * This field will set in
116                                          * ProxyMain:SelectionRequest... */
117 }       drop_info_t;
118     
119 typedef struct {
120     Atom        target;
121     Atom        property;
122 }    target_prop_t;
123
124 typedef struct {
125     BYTE        byte_order;
126     BYTE        protocol_version;
127     CARD16      num_target_lists B16;
128     CARD32      size B32;
129 } motif_targets_t;
130
131 typedef struct {
132     BYTE         message_type;
133     BYTE         byte_order;
134     CARD16       flags;
135     CARD32       time;
136     INT16        x;
137     INT16        y;
138     CARD32       icc_handle;
139     CARD32       src_window;
140 } motif_drop_start_t;
141
142 typedef struct {
143     BYTE        byte_order;
144     BYTE        protocol_version;
145     CARD16      targets_index B16;
146     CARD32      icc_handle B32;
147 } motif_initiator_t;
148
149 typedef struct {
150     BYTE        byte_order;
151     BYTE        protocol_version;
152     BYTE        drag_protocol_style;
153     BYTE        pad1;
154     CARD32      proxy_window B32;
155     CARD16      num_drop_sites B16;
156     CARD16      pad2 B16;
157     CARD32      heap_offset B32;
158 } motif_receiver_t;
159
160 typedef struct _box {
161     short x1, x2, y1, y2;
162 } BOX;
163
164 typedef struct {
165     long size;
166     long numRects;
167     BOX *rects;
168     BOX extents;
169 } REGION;
170
171 typedef struct _site {
172     int screen;
173     unsigned long site_id;
174     Window window_id;
175     unsigned long flags;
176     Region region;
177     struct _site *next;
178 } drop_site_t;
179
180 extern char *ProgramName;
181 extern drop_site_t *MasterSiteList;
182 extern Window proxy_win;
183 extern Atom ATOM_DRAGDROP_INTEREST;
184 extern Atom ATOM_WM_STATE;
185
186 unsigned char *GetInterestProperty();
187
188 static ForwardConversion();
189
190 static drop_info_t drop_table[DROP_TABLE_MAX];
191 Atom ATOM_SUN_DND_TRIGGER;
192 Atom ATOM_SUN_DND_DONE;
193 Atom ATOM_SUN_SELECTION_END;
194 Atom ATOM_SUN_SELECTION_ERROR;
195 Atom ATOM_SUN_DND_ACK;
196 Atom ATOM_MOTIF_DRAG_WIN;
197 Atom ATOM_MOTIF_PROXY_WIN;
198 Atom ATOM_MOTIF_DND_MESS;
199 Atom ATOM_MOTIF_INITIATOR_INFO;
200 Atom ATOM_MOTIF_RECEIVER_INFO;
201 Atom ATOM_MOTIF_SUCCESS;
202 Atom ATOM_MOTIF_FAILURE;
203 Atom ATOM_MOTIF_TARGETS;
204 Atom ATOM_TARGETS;
205 Atom ATOM_MULTIPLE;
206 Window motif_drag_win;
207 char motif_byte_order;
208
209 void 
210 InitializeByteOrder(void)
211 {
212     unsigned int                endian = 1;
213
214     /* if LSByte is set, then hardware uses little endian addressing */
215     if (*((char *)&endian))
216         motif_byte_order = 'l';
217     else
218         motif_byte_order = 'B';
219 }
220
221 /* swap 2 bytes of data if the byte order is different */
222 int
223 Swap2Bytes(char byte_order, CARD16 data)
224 {
225     static CARD16 ret_data;
226     char *old_p = (char *) &data;
227     char *new_p = (char *) &ret_data;
228
229     if (byte_order == motif_byte_order)
230         return data;
231
232     new_p[0] = old_p[1];
233     new_p[1] = old_p[0];
234     return ret_data;
235 }
236         
237 /* swap 4 bytes of data if the byte order is different */
238 int
239 Swap4Bytes(char byte_order, CARD32 data)
240 {
241     static CARD32 ret_data;
242     char *old_p = (char *) &data;
243     char *new_p = (char *) &ret_data;
244
245     if (byte_order == motif_byte_order)
246         return data;
247
248     new_p[0] = old_p[3];
249     new_p[1] = old_p[2];
250     new_p[2] = old_p[1];
251     new_p[3] = old_p[0];
252     return ret_data;
253 }
254         
255
256 /* Get a new drop record */
257 /* use source_handle to indicate whether record is free or in use */
258 static drop_info_t *
259 NewDropInfo(void)
260 {
261     int i, oldest_index=0;
262
263     for(i=0; i<DROP_TABLE_MAX; i++)
264         if (drop_table[i].source_handle == 0) {
265             return drop_table+i;
266         }
267
268     /* if there are no more free records then use the oldest record */
269     for(i=1; i<DROP_TABLE_MAX; i++)
270         if (drop_table[i].time_stamp < drop_table[oldest_index].time_stamp)
271             oldest_index = i;
272
273     return drop_table+oldest_index;
274 }
275
276 /* Search for a drop record */
277 /* use source_handle as the key to the drop_info record */
278 static drop_info_t *
279 GetDropInfo(Window proxy_sel_req_win)
280 {
281     int i;
282
283     for(i=0; i<DROP_TABLE_MAX; i++)
284         if (drop_table[i].proxy_sel_req_win == proxy_sel_req_win) {
285             return drop_table+i;
286         }
287     return NULL;
288 }
289
290 /* Search for a drop record using the proxy_handle as the key */
291 static drop_info_t *
292 GetDropInfoUsingProxyHandle(Atom proxy_handle)
293 {
294     int i;
295
296     for(i=0; i<DROP_TABLE_MAX; i++)
297         if (drop_table[i].proxy_handle == proxy_handle
298             && drop_table[i].source_handle)
299             return drop_table+i;
300     return NULL;
301 }
302
303 /* Delete a drop record */
304 static void
305 ClearDropInfo(Atom proxy_handle)
306 {
307     int i;
308     for(i=0; i<DROP_TABLE_MAX; i++)
309         if (drop_table[i].proxy_handle == proxy_handle) {
310             drop_table[i].source_handle = 0; /* reset flag */
311             break;
312         }
313 }
314             
315 /* get the window that has the atom associated with it */
316 Window 
317 GetAtomWindow(Display *dpy, Window win, Atom atom)
318 {
319     Window              root, parent;
320     Window              *children;
321     unsigned int        nchildren;
322     int                 i;
323     Atom                type = None;
324     int                 format;
325     unsigned long       nitems, after;
326     unsigned char       *data;
327     Window              inf = 0;
328
329     XGetWindowProperty(dpy, win, atom, 0, 0, False, AnyPropertyType,
330                        &type, &format, &nitems, &after, &data);
331     if (type)
332       return win;
333     else {
334         if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren) ||
335             (nchildren == 0))
336           return 0;
337         for (i = nchildren - 1; i >= 0; i--) {
338             if (inf = GetAtomWindow(dpy, children[i], atom))
339               return inf;
340         }
341     }
342     return 0;
343 }
344
345 /* get the window that is a property of the inputed window */
346 Window
347 GetPropertyWindow(Display *dpy, Window in_win, Atom atom)
348 {
349     Atom            type;
350     int             format;
351     unsigned long   lengthRtn;
352     unsigned long   bytesafter;
353     Window         *property = NULL;
354     Window          win = None;
355
356     if ((XGetWindowProperty (dpy,
357                              in_win,
358                              atom,
359                              0L, 1,
360                              False,
361                              AnyPropertyType,
362                              &type,
363                              &format,
364                              &lengthRtn,
365                              &bytesafter,
366                              (unsigned char **) &property) == Success) &&
367          (type == XA_WINDOW) &&
368          (format == 32) &&
369          (lengthRtn == 1)) {
370         win = *property;
371     }
372     if (property) {
373         XFree ((char *)property);
374     }
375
376     return (win);
377 }
378
379 /* startup initialization */
380 void
381 ProxyInit(Display *dpy, Window dsdm_win)
382 {
383     enum { XA_MOTIF_DRAG_WINDOW, XA_MOTIF_DRAG_PROXY_WINDOW,
384            XA_MOTIF_DRAG_AND_DROP_MESSAGE,
385            XA_MOTIF_DRAG_INITIATOR_INFO, XA_MOTIF_DRAG_RECEIVER_INFO,
386            XAXmTRANSFER_SUCCESS, XAXmTRANSFER_FAILURE,
387            XA_MOTIF_DRAG_TARGETS, XA_SUN_DRAGDROP_TRIGGER,
388            XA_SUN_DRAGDROP_DONE, XA_SUN_SELECTION_END, XA_SUN_SELECTION_ERROR,
389            XA_SUN_DRAGDROP_ACK, XATARGETS, XAMULTIPLE,  NUM_ATOMS };
390     static char* atom_names[] = {
391            "_MOTIF_DRAG_WINDOW", "_MOTIF_DRAG_PROXY_WINDOW",
392            "_MOTIF_DRAG_AND_DROP_MESSAGE",
393            "_MOTIF_DRAG_INITIATOR_INFO", "_MOTIF_DRAG_RECEIVER_INFO",
394            "XmTRANSFER_SUCCESS", "XmTRANSFER_FAILURE",
395            "_MOTIF_DRAG_TARGETS", "_SUN_DRAGDROP_TRIGGER",
396            "_SUN_DRAGDROP_DONE", "_SUN_SELECTION_END", "_SUN_SELECTION_ERROR",
397            "_SUN_DRAGDROP_ACK", "TARGETS", "MULTIPLE" };
398
399     int i;
400     enum { ATOM_BUF_LEN = 25 };
401     char buf[ATOM_BUF_LEN * DROP_TABLE_MAX];
402     char *buf_names[DROP_TABLE_MAX];
403     Atom buf_atoms[DROP_TABLE_MAX];
404     XSetWindowAttributes attr;
405     Atom atoms[NUM_ATOMS];
406
407     InitializeByteOrder();
408
409     /* make motif_drag_win and proxy_win persistent */
410     XSetCloseDownMode(dpy, RetainPermanent);
411     XInternAtoms(dpy, atom_names, NUM_ATOMS, False, atoms);
412
413     ATOM_MOTIF_DRAG_WIN = atoms[XA_MOTIF_DRAG_WINDOW];
414     ATOM_MOTIF_PROXY_WIN = atoms[XA_MOTIF_DRAG_PROXY_WINDOW];
415     ATOM_MOTIF_DND_MESS = atoms[XA_MOTIF_DRAG_AND_DROP_MESSAGE];
416     ATOM_MOTIF_INITIATOR_INFO = atoms[XA_MOTIF_DRAG_INITIATOR_INFO];
417     ATOM_MOTIF_RECEIVER_INFO = atoms[XA_MOTIF_DRAG_RECEIVER_INFO];
418     ATOM_MOTIF_SUCCESS = atoms[XAXmTRANSFER_SUCCESS];
419     ATOM_MOTIF_FAILURE = atoms[XAXmTRANSFER_FAILURE];
420     ATOM_MOTIF_TARGETS = atoms[XA_MOTIF_DRAG_TARGETS];
421
422     ATOM_SUN_DND_TRIGGER = atoms[XA_SUN_DRAGDROP_TRIGGER];
423     ATOM_SUN_DND_DONE = atoms[XA_SUN_DRAGDROP_DONE];
424     ATOM_SUN_SELECTION_ERROR = atoms[XA_SUN_SELECTION_ERROR];
425     ATOM_SUN_SELECTION_END = atoms[XA_SUN_SELECTION_END];
426     ATOM_SUN_DND_ACK = atoms[XA_SUN_DRAGDROP_ACK];
427
428     ATOM_TARGETS = atoms[XATARGETS];
429     ATOM_MULTIPLE = atoms[XAMULTIPLE];
430
431     for (i=0; i<DROP_TABLE_MAX; i++) {
432         buf_names[i] = buf + (ATOM_BUF_LEN * i);
433         sprintf(buf_names[i], "DND_PROXY_HANDLE_%d", i);
434     }
435     XInternAtoms(dpy, buf_names, DROP_TABLE_MAX, False, buf_atoms);
436
437     for (i=0; i<DROP_TABLE_MAX; i++) {
438         drop_table[i].proxy_handle = buf_atoms[i];
439         drop_table[i].proxy_sel_req_win = 
440             XCreateWindow(dpy,
441                           DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0,
442                           InputOnly, CopyFromParent, 0, &attr);
443     }
444     motif_drag_win = GetPropertyWindow(dpy, DefaultRootWindow(dpy),
445                              ATOM_MOTIF_DRAG_WIN);
446
447     /* if motif_drag_win doesn't exist then define and declare it */
448     if (motif_drag_win == None) {
449         motif_drag_win = dsdm_win;
450         XChangeProperty (dpy,
451                      DefaultRootWindow(dpy),
452                      ATOM_MOTIF_DRAG_WIN,
453                      XA_WINDOW,
454                      32,
455                      PropModeReplace,
456                      (unsigned char *) &motif_drag_win,
457                      1);
458
459     }
460
461     /* define and declare the proxy_window */
462     proxy_win = dsdm_win;
463     XChangeProperty(dpy, motif_drag_win, 
464                     ATOM_MOTIF_PROXY_WIN, /* property */
465                     XA_WINDOW, /* type */
466                     32,               /* format */
467                     PropModeReplace, /* mode */
468                     (unsigned char *) &proxy_win, /* data */
469                     1
470                     );
471
472 #ifdef DEBUG
473     printf("drag_win=0x%lx, proxy_win=0x%lx\n", motif_drag_win, proxy_win);
474 #endif
475     /*  Watch for mapping of top level windows */
476     XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureNotifyMask);
477
478     /*  Watch for someone taking away proxy_win ownership */
479     XSelectInput(dpy, proxy_win, PropertyChangeMask);
480
481     for(i=0; i<DROP_TABLE_MAX; i++)
482         XSetSelectionOwner(dpy, drop_table[i].proxy_handle,
483                            proxy_win, CurrentTime);
484 }
485
486 /* Place the motif receiver property on the Openlook receiver's top
487    level window */
488 int
489 AdvertiseMotifDropSite(Display *dpy, Window win)
490 {
491     motif_receiver_t receiver_info;
492     unsigned long nitems;
493     CARD32 *data;
494     int motif_receiver = False;
495
496 #define OL_PREVIEW_INDEX 4
497 #define MOTIF_RECEIVER_FLAG     0x80000000
498     /* make sure receiver is not a Motif application */
499     data = (CARD32 *) GetInterestProperty(dpy, win, &nitems);
500     if (data != NULL) {
501         if (nitems > OL_PREVIEW_INDEX &&
502             (data[OL_PREVIEW_INDEX] & MOTIF_RECEIVER_FLAG))
503             motif_receiver = True;
504         XFree(data);
505     }
506     if (motif_receiver)
507         return 0;
508
509 #ifdef DEBUG
510     printf("AdvertiseMotifDropSite\n");
511 #endif
512
513     receiver_info.byte_order = motif_byte_order;
514     receiver_info.protocol_version = MOTIF_DRAG_PROTOCOL_VERSION;
515     receiver_info.drag_protocol_style = XmDRAG_DROP_ONLY;
516     receiver_info.proxy_window = proxy_win;
517     receiver_info.num_drop_sites = 0;  /* not used in drag_drop_only mode */
518     receiver_info.heap_offset = 0;     /* not used in drag_drop_only mode */
519
520     XChangeProperty(dpy,
521                     win,
522                     ATOM_MOTIF_RECEIVER_INFO, /* property */
523                     ATOM_MOTIF_RECEIVER_INFO, /* type */
524                     8,               /* format */
525                     PropModeReplace, /* mode */
526                     (unsigned char *) &receiver_info, /* data */
527                     sizeof(motif_receiver_t)  /* size of data in bytes */
528                     );
529     return 1;
530 }
531
532 /* Determine if the drop coordinate is within a rectangle */
533 static char
534 InRect(int drop_x, int drop_y, int x, int y, unsigned int width, unsigned int height)
535 {
536     if ((drop_x >= x) && (drop_x <= (x+width)) &&
537         (drop_y >= y) && (drop_y <= (y+height)))
538         return True;
539     else
540         return False;
541 }
542
543 #define DRAGDROP_VERSION        0       /* OLIT dnd version */
544 #define INTEREST_RECT           0
545 #define INTEREST_WINDOW         1
546 #define NEXTWORD(result) if (index >= nitems)   {               \
547                              XFree(data);                       \
548                              return;                            \
549                          }                                      \
550                          (result) = data[index++]
551
552 /* get the drop site given the window and coordinate */
553 static void
554 GetOlitDropSite(Display *dpy, Window top_level_win, INT16 drop_x, INT16 drop_y,
555                 unsigned long *ret_site_id, Window *ret_event_win)
556 {
557     unsigned long *data, nitems, version, nsites, event_win, site_id, flags;
558     unsigned long areatype, nrects, areawin;
559     int index = 0;
560     int i,j, junk;
561     char done = False;
562     Window wjunk;
563     int x,y;
564     unsigned int width, height, border, ujunk;
565     Window root_win;
566
567     *ret_event_win = *ret_site_id = 0;
568     data = (unsigned long *) GetInterestProperty(dpy, top_level_win, &nitems);
569     if (data == NULL)   return;
570
571     /* get root window */
572     if (XGetGeometry(dpy, top_level_win, &root_win, &junk, &junk,
573                      &ujunk, &ujunk, &ujunk, &ujunk) == 0)
574         return;
575
576     NEXTWORD(version);
577     if (version != DRAGDROP_VERSION)    return;
578     NEXTWORD(nsites);
579     for (i=0; i<nsites && !done; ++i) {
580         NEXTWORD(event_win);    NEXTWORD(site_id);      NEXTWORD(flags);
581         NEXTWORD(areatype);
582         switch (areatype) {
583         case INTEREST_RECT:
584             NEXTWORD(nrects);
585             for (j=0; j<nrects && !done; ++j) {
586                 NEXTWORD(x);
587                 NEXTWORD(y);
588                 NEXTWORD(width);
589                 NEXTWORD(height);
590                 if (XTranslateCoordinates(dpy, top_level_win, root_win, x, y,
591                                              &x, &y, &wjunk))
592                     done = InRect((int)drop_x,(int)drop_y, x, y, width,height);
593             }
594             break;
595         case INTEREST_WINDOW:
596             NEXTWORD(nrects);
597             for (j=0; j<nrects && !done; ++j) {
598                 NEXTWORD(areawin);
599                 if (0 == XGetGeometry(dpy, areawin, &wjunk, &junk, &junk,
600                                       &width, &height, &border, &ujunk)) {
601                     fprintf(stderr,
602                             "%s: XGetGeometry failed on window 0x%lx\n",
603                             ProgramName, areawin);
604                     return;
605                 }
606                 if (XTranslateCoordinates(dpy, areawin, root_win, 0, 0,
607                                              &x, &y, &wjunk)) {
608                     x = x - border;
609                     y = y - border;
610                     width = width + border;
611                     height = height + border;
612                     done = InRect((int)drop_x, (int)drop_y, x, y,width,height);
613                 }
614             }
615             break;
616         default:
617             fprintf(stderr,
618                     "%s: unknown site area type on window 0x%lx\n",
619                     ProgramName, top_level_win);
620             return;
621         }
622     }
623     if (done) {
624         *ret_site_id = site_id;
625         *ret_event_win = event_win;
626     }
627 }
628
629 #define MOTIF_DROP_MOVE_ACTION  (1 << 0)
630 #define MOTIF_DROP_COPY_ACTION  (1 << 1)
631 #define MOTIF_DROP_LINK_ACTION  (1 << 2)
632 #define MOTIF_DROP_ALLOWED_ACTIONS        0x0F00
633 #define OLIT_DROP_ACKNOWLEDGE_ACTION    (1 << 1)
634 #define OLIT_DROP_TRANSIENT_ACTION      (1 << 2)
635 #define OLIT_DROP_MOVE_ACTION   (1 << 0)
636 #define OLIT_DROP_COPY_ACTION   0
637 #define OLIT_DROP_LINK_ACTION   (1 << 10)
638
639 /* translate an OLIT operation to a Motif operation */
640 static CARD16
641 ConvertOlitAction(CARD32 olit_action)
642 {
643     CARD16 motif_action;
644
645     motif_action = MOTIF_DROP_COPY_ACTION | (MOTIF_DROP_COPY_ACTION << 8);
646
647     if (olit_action & OLIT_DROP_MOVE_ACTION)
648         motif_action |= MOTIF_DROP_MOVE_ACTION | (MOTIF_DROP_MOVE_ACTION << 8);
649     else if (olit_action & OLIT_DROP_LINK_ACTION)
650         motif_action |= MOTIF_DROP_LINK_ACTION | (MOTIF_DROP_LINK_ACTION << 8);
651
652     return motif_action;
653 }
654
655 /* translate a Motif operation to an OLIT operation */
656 static CARD32
657 ConvertMotifAction(CARD16 motif_action)
658 {
659     CARD32 olit_action;
660
661     olit_action = OLIT_DROP_TRANSIENT_ACTION;
662     if (motif_action & MOTIF_DROP_MOVE_ACTION)
663         olit_action |= OLIT_DROP_MOVE_ACTION;
664     else if (motif_action & MOTIF_DROP_COPY_ACTION)
665         olit_action |= OLIT_DROP_COPY_ACTION;
666     else if (motif_action & MOTIF_DROP_LINK_ACTION)
667         olit_action |= OLIT_DROP_LINK_ACTION;
668     else
669         olit_action = 0;
670     return olit_action;
671 }
672
673 /* Process Motif ClientMessage */
674 void
675 HandleMotifMessage(Display *dpy, XClientMessageEvent *event)
676 {
677     motif_drop_start_t info;
678     drop_info_t *drop_info;
679     Window dest_win;
680
681     dest_win = event->window;
682     info.message_type = event->data.b[0];
683     info.byte_order = event->data.b[1];
684
685     if (info.message_type & MOTIF_RECEIVER_BIT) {
686         /* receiver message
687            If initiator is Motif then forward it this message. */
688         if (event->window != proxy_win)
689             XSendEvent(dpy, event->window, False,
690                        NoEventMask, (XEvent *) event);
691     }
692     else {
693         /* initiator message */
694         if ((info.message_type & MOTIF_MESSAGE_TYPE_MASK)== MOTIF_DROP_START) {
695             unsigned long site_id;
696             Window event_win;
697             XClientMessageEvent out_event;
698             CARD32      olit_action;
699
700             /* receiver will never be MOTIF because MOTIF receivers
701                don't set the proxy field of RECEIVER_INFO atom
702                */
703
704             info.flags = Swap2Bytes(info.byte_order, event->data.s[1]);
705
706             /* since dropSiteStatus can't be determined in a short amount
707                of time, always return a valid drop site */
708             info.flags &= MOTIF_DROP_SITE_STATUS_MASK;
709             info.flags |= MOTIF_DROP_SITE_STATUS_VALID;
710
711             info.time = Swap4Bytes(info.byte_order, event->data.l[1]);
712             info.x = Swap2Bytes(info.byte_order, event->data.s[4]);
713             info.y = Swap2Bytes(info.byte_order, event->data.s[5]);
714             info.icc_handle = Swap4Bytes(info.byte_order, event->data.l[3]);
715             info.src_window = Swap4Bytes(info.byte_order, event->data.l[4]);
716
717             /* send a drop start ack here
718                to give ourselves more time to process the drop
719                because GetOlitDropSite or 
720                selection processing might take a long time
721                */
722             info.message_type = event->data.b[0] |= MOTIF_RECEIVER_BIT;
723             event->data.s[1] = Swap2Bytes(info.byte_order, info.flags);
724             event->window = info.src_window;
725             XSendEvent(dpy, event->window, False,
726                        NoEventMask, (XEvent *) event);
727
728             /* get olit drop site */
729             GetOlitDropSite(dpy, 
730                             dest_win, info.x, info.y, &site_id, &event_win);
731             drop_info = NewDropInfo();
732             olit_action = ConvertMotifAction(info.flags);
733             if (event_win && olit_action) {
734                 drop_info->source_handle = info.icc_handle;
735                 drop_info->time_stamp = info.time;
736
737                 out_event.type = ClientMessage;
738                 out_event.send_event = True;
739                 out_event.display = dpy;
740                 out_event.window = event_win;
741                 out_event.message_type = ATOM_SUN_DND_TRIGGER;
742                 out_event.format = 32;
743
744                 out_event.data.l[OLIT_DROP_SELECTION]= drop_info->proxy_handle;
745                 out_event.data.l[OLIT_DROP_TIME] = info.time;
746                 out_event.data.l[OLIT_DROP_COORDINATE] = 
747                     (info.x << 16) | info.y;
748                 out_event.data.l[OLIT_DROP_SITE_ID] = site_id;
749                 out_event.data.l[OLIT_DROP_OPERATION] = olit_action;
750                 XSendEvent(dpy, out_event.window, False,
751                        NoEventMask, (XEvent *) &out_event);
752             }
753             else {
754                 /* tell motif initiator that the drop failed */
755                 XConvertSelection(dpy,
756                                   info.icc_handle,
757                                   ATOM_MOTIF_FAILURE,
758                                   None,
759                                   proxy_win,
760                                   info.time);
761                 
762             }
763         }
764     }
765 }
766
767 /* Process OLIT ClientMessage */
768 static void
769 HandleOlitTrigger(Display *dpy, XClientMessageEvent *event)
770 {
771     drop_site_t *site;
772     char success = False;
773     Atom source_handle = event->data.l[OLIT_DROP_SELECTION];
774     Time time_stamp = event->data.l[OLIT_DROP_TIME];
775
776     /* proxy agent only gets ATOM_SUN_DND_TRIGGER if
777        receiver is motif */
778     
779     /* map site_id to site_window */
780     for (site = MasterSiteList; site != NULL; site = site->next) {
781         if (site->site_id == event->data.l[OLIT_DROP_SITE_ID]) {
782             drop_info_t *drop_info = NewDropInfo();
783             
784             drop_info->motif_action = 
785                 ConvertOlitAction(event->data.l[OLIT_DROP_OPERATION]);
786             drop_info->receiver_win = site->window_id;
787             drop_info->source_handle = source_handle;
788             drop_info->trigger_x = (event->data.l[OLIT_DROP_COORDINATE]) >> 16;
789             drop_info->trigger_y = 
790                 (event->data.l[OLIT_DROP_COORDINATE]) & 0x0000ffff;
791             drop_info->time_stamp = time_stamp;
792
793             if ((event->data.l[OLIT_DROP_OPERATION])
794                 & OLIT_DROP_ACKNOWLEDGE_ACTION) {
795                 XConvertSelection(dpy,
796                               drop_info->source_handle, /* selection */
797                               ATOM_SUN_DND_ACK,
798                               ATOM_SUN_DND_ACK, /* property */
799                               drop_info->proxy_sel_req_win,
800                               drop_info->time_stamp /* time */
801                               );
802             }
803
804             /* get olit initiator targets */
805             XConvertSelection(dpy,
806                               drop_info->source_handle, /* selection */
807                               ATOM_TARGETS,
808                               drop_info->proxy_handle,  /* property */
809                               drop_info->proxy_sel_req_win,
810                               drop_info->time_stamp /* time */
811                               );
812             success = True;
813             break;
814         }
815     }               
816     if (!success)
817         /* tell olit initiator we are done */
818         XConvertSelection(dpy, source_handle,
819                           ATOM_SUN_DND_DONE,
820                           ATOM_SUN_DND_DONE, /* property */
821                           proxy_win,
822                           time_stamp);
823 }
824
825
826 /* Place the Motif initiator property on the initiator's top level window */
827 static void
828 UpdateInitiatorAtom(Display *dpy, Window win, Atom channel, CARD16 targets_index)
829 {
830     motif_initiator_t initiator_info;
831
832     initiator_info.byte_order = motif_byte_order;
833     initiator_info.protocol_version = MOTIF_DRAG_PROTOCOL_VERSION;
834     initiator_info.targets_index = targets_index;
835     initiator_info.icc_handle = channel;
836
837     XChangeProperty(dpy, win,
838                     channel, /* property */
839                     ATOM_MOTIF_INITIATOR_INFO, /* type */
840                     8,               /* format */
841                     PropModeReplace, /* mode */
842                     (unsigned char *) &initiator_info, /* data */
843                     sizeof(motif_initiator_t) /* size of data in bytes */
844                     );
845 }
846
847 /* Send Motif DROP_START ClientMessage to receiver */
848 void
849 SendStartDrop(Display *dpy, Window src_win, drop_info_t *drop_info)
850 {
851     motif_drop_start_t info;
852     XClientMessageEvent event;
853
854     info.message_type = MOTIF_DROP_START;
855     info.byte_order = motif_byte_order;
856     info.flags = drop_info->motif_action;
857     info.time = drop_info->time_stamp;
858     info.x = drop_info->trigger_x;
859     info.y = drop_info->trigger_y;
860     info.icc_handle = drop_info->proxy_handle;
861     info.src_window = src_win;
862
863     event.type = ClientMessage;
864     event.send_event = True;
865     event.display = dpy;
866     event.window = drop_info->receiver_win;
867     event.message_type = ATOM_MOTIF_DND_MESS;
868     event.format = 8;
869     memcpy(&event.data, &info, sizeof(info));
870     XSendEvent(dpy, event.window, False, NoEventMask, (XEvent *) &event);
871 }
872
873 /* Compare data of type Atom */
874 static int
875 AtomCompare(const void *a1, const void *a2)
876 {
877     const Atom *atom1 = a1;
878     const Atom *atom2 = a2;
879
880     return(*atom1 - *atom2);
881 }
882
883 /* Get the Motif TARGETS */
884 static void
885 GetTargetsList(Display *dpy, motif_targets_t **targets_list_p)
886 {
887     motif_targets_t     *targets_list;
888     Atom type;
889     int format, status;
890     unsigned long length, bytes_after;
891
892     status = XGetWindowProperty(dpy,
893                              motif_drag_win,
894                              ATOM_MOTIF_TARGETS,
895                              0L, GET_PROPERTY_MAX,
896                              False,
897                              ATOM_MOTIF_TARGETS,
898                              &type,
899                              &format,
900                              &length,
901                              &bytes_after,
902                              (unsigned char **) &targets_list);
903     if ((status != Success) || (type != ATOM_MOTIF_TARGETS) || (length == 0)) {
904         /* allocate targets_list of zero targets */
905         targets_list = (motif_targets_t *) malloc(sizeof(motif_targets_t));
906         targets_list->byte_order = motif_byte_order;
907         targets_list->protocol_version = MOTIF_DRAG_PROTOCOL_VERSION;
908         targets_list->num_target_lists = 0;
909         targets_list->size = sizeof(motif_targets_t);
910     }
911     *targets_list_p = targets_list;
912 }
913
914 static void
915 MatchTargets(motif_targets_t *targets_list, Atom *atoms, unsigned long atom_cnt, CARD16 *targets_index_p)
916 {
917     int target_index;
918     CARD32      *data;
919     int num_sub_list_items, sub_list_index;
920     CARD16      *num_sub_p;
921     char byte_order = targets_list->byte_order;
922     int num_target_lists;
923     CARD32 target, tmp_data;
924
925     num_target_lists = Swap2Bytes(byte_order, targets_list->num_target_lists);
926     data = (CARD32 *) (targets_list+1);
927     for (target_index=0; target_index < num_target_lists; target_index++) {
928         num_sub_p = (CARD16 *) data;
929         num_sub_list_items = (int) Swap2Bytes(byte_order, *num_sub_p);
930         data = (CARD32 *) (num_sub_p+1);
931
932         if (num_sub_list_items == atom_cnt) {
933             for (sub_list_index=0; sub_list_index < num_sub_list_items;
934                  sub_list_index++) {
935                /* address isn't long word aligned so copy one byte at a time */
936                 memcpy(&tmp_data, data+sub_list_index, 4);
937                 target = Swap4Bytes(byte_order, tmp_data);
938                 if (target != atoms[sub_list_index])
939                     break;
940             }
941             /* if all the atoms match then we got a match */
942             if (sub_list_index >= num_sub_list_items) {
943                 break;
944             }
945         }
946         data += num_sub_list_items;
947     }
948
949     *targets_index_p = target_index;
950 }
951
952 /* Put OLIT targets on the Motif TARGETS targets list */
953 static void
954 GetTargetsIndex(Display *dpy, unsigned char *in_data, unsigned long atom_cnt, CARD16 *targets_index_p)
955 {
956     Atom                *atoms = (Atom *) in_data;
957     motif_targets_t     *new_list, *targets_list;
958     int                 i, old_size, new_size;
959     CARD16              *target_cnt;
960     CARD32              *target_data, tmp_data;
961     int                 malloc_length;
962     motif_initiator_t   initiator_info;
963     char                byte_order;
964     CARD16              num_target_lists;
965
966 #ifdef DEBUG
967     for (i=0; i<atom_cnt; i++) {
968         printf("GetTargetsIndex: target[%d]=%s\n", i, a2s(atoms[i]));
969     }
970 #endif
971
972     /* search existing targets list for a match, otherwise add our
973        targets to the list */
974     qsort((void *)atoms, (size_t)atom_cnt, (size_t)sizeof(Atom), AtomCompare);
975     GetTargetsList(dpy, &targets_list);
976     MatchTargets(targets_list, atoms, atom_cnt, targets_index_p);
977     byte_order = targets_list->byte_order;
978     num_target_lists = Swap2Bytes(byte_order,targets_list->num_target_lists);
979     if (*targets_index_p < num_target_lists) {
980         free(targets_list);
981         return;
982     }
983
984     /* no match, add our targets to the list */
985     old_size = Swap4Bytes(byte_order, targets_list->size);
986     /* new size = old_list_size + target_cnt(CARD16) + targets(CARD32) */
987     new_size = old_size + sizeof(CARD16) + (sizeof(CARD32) * atom_cnt);
988     new_list = (motif_targets_t *) 
989         realloc(targets_list, new_size);
990
991     /* change the new data's byte order to the existing data's byte order */
992     /* put data in motif target list format */
993     num_target_lists++;
994     new_list->num_target_lists = Swap2Bytes(byte_order, num_target_lists);
995     new_list->size = Swap4Bytes(byte_order, new_size);
996
997     target_cnt = (CARD16 *) (((BYTE *) new_list) + old_size);
998     *target_cnt = Swap2Bytes(byte_order, atom_cnt); /* target_cnt */
999     target_data = (CARD32 *) (target_cnt + 1);
1000     for (i=0; i<atom_cnt; i++) {
1001         /* address isn't long word aligned so copy one byte at a time */
1002         tmp_data = Swap4Bytes(byte_order, atoms[i]);
1003         memcpy(target_data+i, &tmp_data, 4);
1004     }
1005
1006     /* overwrite existing targets_list */
1007     XChangeProperty(dpy, motif_drag_win, ATOM_MOTIF_TARGETS,
1008                     ATOM_MOTIF_TARGETS, 8,
1009                     PropModeReplace,
1010                     (unsigned char *) new_list, new_size);
1011 }
1012
1013 /* 
1014   * update target list property
1015   * advertise psuedo motif initiator info property
1016   * send DROP_START message to motif receiver
1017   */
1018 static void
1019 ContinueHandleOlitTrigger(Display *dpy, unsigned char *data, unsigned long length, drop_info_t *drop_info)
1020 {
1021     CARD16              targets_index;
1022     Window psuedo_motif_initiator_win;
1023
1024     GetTargetsIndex(dpy, data, length, &targets_index);
1025     psuedo_motif_initiator_win = proxy_win;
1026     UpdateInitiatorAtom(dpy, psuedo_motif_initiator_win, 
1027                         drop_info->proxy_handle, targets_index);
1028     SendStartDrop(dpy, psuedo_motif_initiator_win, drop_info);
1029 }
1030
1031 /* Get OLIT initiator's TARGETS */
1032 void
1033 ProcessOlitInitiatorConversion(XSelectionEvent *event, drop_info_t *drop_info)
1034 {
1035     Atom type;
1036     int format;
1037     unsigned long length, bytes_after;
1038     unsigned char *data;
1039
1040
1041     /* XView needs this */
1042     if (event->target == ATOM_SUN_DND_ACK)
1043         XChangeProperty(event->display, event->requestor, event->property,
1044                   event->target, 32, PropModeReplace, NULL, 0);
1045
1046     /* get OLIT targets
1047        sometimes type is TARGETS and sometimes type is ATOM so don't bother
1048        checking type */
1049     if ((event->target == ATOM_TARGETS) &&
1050         event->property && (XGetWindowProperty (event->display,
1051                              event->requestor,
1052                              event->property,
1053                              0L, GET_PROPERTY_MAX,
1054                              False,
1055                              AnyPropertyType,
1056                              &type,
1057                              &format,
1058                              &length,
1059                              &bytes_after,
1060                              (unsigned char **) &data) == Success)
1061         && length) {
1062
1063         ContinueHandleOlitTrigger(event->display, data, length, drop_info);
1064         XFree(data);
1065     }
1066 }
1067
1068 /* copy a property and the associated data from one window to another.
1069    Returns True if successful else returns False */
1070 static int
1071 CopyProperty(Display *dpy, Atom property, Window old_win, Window new_win)
1072 {
1073     Atom type;
1074     int format;
1075     unsigned long length, bytes_after;
1076     unsigned char *data;
1077
1078     if (property && (XGetWindowProperty (dpy, old_win, property,
1079                              0L, GET_PROPERTY_MAX,
1080                              False, 
1081                              AnyPropertyType,
1082                              &type,
1083                              &format,
1084                              &length,
1085                              &bytes_after,
1086                              (unsigned char **) &data) == Success)
1087         && format) {
1088         XChangeProperty(dpy, new_win, property,
1089                         type, format, PropModeReplace, data, (int) length);
1090         XFree(data);
1091         return True;
1092     }
1093     else
1094         return False;
1095 }
1096     
1097 /* copy the targets property and the associated data 
1098    from one window to another.
1099    Returns True if we get ATOM_SUN_DND_DONE or ATOM_SUN_SELECTION_END */
1100 static int
1101 CopyTargets(Display *dpy, Atom prop, Window old_win, Window new_win)
1102 {
1103     Atom type;
1104     int format;
1105     unsigned long length, bytes_after;
1106     unsigned char *data;
1107     target_prop_t *target_info;
1108     int i;
1109     int ol_done = False;
1110
1111     if ((XGetWindowProperty (dpy, old_win, prop,
1112                              0L, GET_PROPERTY_MAX,
1113                              False, 
1114                              AnyPropertyType,
1115                              &type,
1116                              &format,
1117                              &length,
1118                              &bytes_after,
1119                              &data) == Success)
1120         && format) {
1121         target_info = (target_prop_t *) data;
1122         for(i=0; i < (length / 2); i++, target_info++) {
1123             DPRINTF(("CopyTargets: targets[%d]=%s\n", i, a2s(target_info->target)));
1124             CopyProperty(dpy, target_info->property, old_win, new_win);
1125             if (target_info->target == ATOM_SUN_DND_DONE ||
1126                 (target_info->target == ATOM_SUN_SELECTION_END))
1127                 ol_done = True;
1128         }
1129         XFree(data);
1130     }
1131     return ol_done;
1132 }
1133     
1134 /* Forward the SelectionNotify to the receiver.
1135    Returns True if we get ATOM_SUN_DND_DONE or ATOM_SUN_SELECTION_END */
1136 static
1137 ForwardConversion(XSelectionEvent *event, drop_info_t *drop_info)
1138 {
1139     int ol_done = False;
1140
1141     if (CopyProperty(event->display, event->property,
1142                      event->requestor, drop_info->source_win)) {
1143
1144         if (event->target == ATOM_MULTIPLE) {
1145             /* get target name and property pair
1146                copy all these properties */
1147             ol_done = CopyTargets(event->display, event->property,
1148                                   event->requestor, drop_info->source_win);
1149         }
1150     }
1151     else {
1152         /* initiator couldn't convert property, tell receiver this */
1153         event->property = 0;
1154     }
1155
1156     event->requestor = drop_info->source_win;
1157     event->selection = drop_info->proxy_handle;
1158     XSendEvent(event->display, event->requestor, False, 0, (XEvent*)event);
1159     return ol_done;
1160 }
1161
1162 /* This routine handles the selectionrequest for the target MULTIPLE.
1163    XtGetSelectionValues generates a selection request with the target
1164    MULTIPLE. This is how multiple targets selection
1165    (a selection request that asks for multiple targets) work:
1166    Window A calls XtGetSelectionValues
1167    Xt places a property on window A which contains all the targets.
1168    Xt asks selection owner of selection 1 to convert.
1169    Window B which is the selection owner of selection 1
1170        gets a selectionrequest.
1171    Window B reads the property on window A to get the targets
1172        and then converts them.
1173    Window B places the converted targets data in properties on window A.
1174    Window B places the target names and the properties holding
1175       the target data (target/property pair) in a property on window A.
1176
1177    With single target selection (XtGetSelectionValue), window B would
1178    get the target from the selectionrequest event structure and convert it.
1179
1180    The following is how the proxy agent behaves when there is a multiple
1181    targets selection:
1182    Window A calls XtGetSelectionValues
1183    Xt places a property on window A which contains all the targets.
1184    Xt asks selection owner of selection 1 to convert.
1185    Proxy agent which is the selection owner of selection 1
1186       gets a selectionrequest.
1187    Proxy agent reads the property on window A to get the targets.
1188    Proxy agent places a property on itself which contains all the targets.
1189    Proxy agent asks selection owner of selection 2 to convert.
1190    Window B which is the selection owner of selection 2 
1191       gets a selectionrequest.
1192    Window B reads the property on proxy agent to get the targets
1193    and then converts them.
1194    Window B places the converted targets data in properties on proxy agent.
1195    Window B places the target names and the properties holding
1196    the target data in a property proxy agent.
1197    Proxy agent reads the targets data and places the targets data
1198       in a property on window A.
1199    Proxy agent reads the target names and the properties holding
1200       the target data and places this info in a property on window A.
1201 */
1202 void
1203 ForwardMultpleSelectionRequest(XSelectionRequestEvent *event, drop_info_t *drop_info)
1204 {
1205     int format, status;
1206     unsigned long length, bytes_after;
1207     unsigned char *data;
1208
1209     drop_info->source_win = event->requestor;
1210
1211     if (CopyProperty(event->display, event->property, 
1212                      event->requestor, drop_info->proxy_sel_req_win)) {
1213
1214         /* change requestor and selection */
1215         event->requestor = drop_info->proxy_sel_req_win;
1216         event->selection = drop_info->source_handle;
1217
1218         /* forward selection request to initiator */
1219         XConvertSelection(event->display,
1220                           event->selection,
1221                           event->target,
1222                           event->property,
1223                           event->requestor,
1224                           event->time
1225                           );
1226     }
1227 }
1228
1229
1230 /* tell the initiator to clean up and do internal cleanup */
1231 void
1232 DndDone(Display *dpy, Atom status, drop_info_t *drop_info)
1233 {
1234     XConvertSelection(dpy,
1235                       drop_info->source_handle,
1236                       status,
1237                       drop_info->proxy_handle,
1238                       drop_info->proxy_sel_req_win,
1239                       drop_info->time_stamp);
1240
1241     /* XView needs this */
1242     if (status == ATOM_SUN_DND_DONE)
1243         XConvertSelection(dpy,
1244                       drop_info->source_handle,
1245                       ATOM_SUN_SELECTION_END,
1246                       drop_info->proxy_handle,
1247                       drop_info->proxy_sel_req_win,
1248                       drop_info->time_stamp);
1249 }
1250
1251 void
1252 ProxyMain(Display *dpy, XEvent *event)
1253 {
1254     switch(event->type) {
1255     case ClientMessage:
1256 #ifdef DEBUG
1257         printf("ClientMessage\n");
1258 #endif
1259         if (event->xclient.message_type == ATOM_SUN_DND_TRIGGER)
1260             HandleOlitTrigger(dpy, &event->xclient);
1261         else if (event->xclient.message_type == ATOM_MOTIF_DND_MESS)
1262             HandleMotifMessage(dpy, &event->xclient);
1263         break;
1264
1265     case MapNotify: {
1266         Window client_win;
1267
1268         client_win = GetAtomWindow(dpy, event->xcreatewindow.window,
1269                                      ATOM_WM_STATE);
1270         if (client_win) {
1271             /* if client has olit dnd interest then attach motif dnd
1272                interest to client */
1273             if (GetAtomWindow(dpy, client_win, ATOM_DRAGDROP_INTEREST)) {
1274                 AdvertiseMotifDropSite(dpy, client_win);
1275             }
1276             /* look for PropertyChange because olit receivers
1277                don't always declare ATOM_DRAGDROP_INTEREST at MapNotify time
1278                */
1279             XSelectInput(dpy, client_win, PropertyChangeMask);
1280         }
1281         break;
1282     }
1283
1284     case PropertyNotify:
1285 /*      printf("PropertyNotify, win=0x%lx\n", event->xproperty.window);*/
1286         /* if a OLIT drop site is advertised
1287            then advertise MOTIF drop site also */
1288         if (event->xproperty.atom == ATOM_DRAGDROP_INTEREST) {
1289             AdvertiseMotifDropSite(dpy, event->xproperty.window);
1290         }
1291         else if (event->xproperty.atom == ATOM_MOTIF_PROXY_WIN) {
1292             if ((motif_drag_win == event->xproperty.window) &&
1293                 (proxy_win != GetPropertyWindow(dpy, motif_drag_win,
1294                              ATOM_MOTIF_PROXY_WIN)))
1295                 /* proxy_win has been stolen by another client,
1296                    inform user */
1297
1298                 /* maybe the following should be a X message */
1299                 fprintf(stderr, "%s: lost proxy control\n", ProgramName);
1300         }
1301
1302         /* In the future, we might want to handle the case where
1303          * a client removes the ATOM_DRAGDROP_INTEREST or the
1304          * ATOM_MOTIF_RECEIVER_INFO atom
1305           */
1306         break;
1307
1308     case SelectionRequest: {
1309         drop_info_t *drop_info;
1310 #ifdef DEBUG
1311         printf("SelectionRequest, selection=%s, target=%s, prop=%s, win=0x%lx, time=%ld\n",
1312                    a2s(event->xselectionrequest.selection),
1313                    a2s(event->xselectionrequest.target),
1314                    a2s(event->xselectionrequest.property),
1315                    event->xselectionrequest.requestor,
1316                    event->xselectionrequest.time
1317                );
1318 #endif
1319         drop_info = GetDropInfoUsingProxyHandle
1320             (event->xselectionrequest.selection);
1321         if (drop_info == NULL)
1322             /* unknown selection, ignore the SelectionRequest */
1323             break;
1324         if (event->xselectionrequest.target == ATOM_SUN_DND_DONE ||
1325             (event->xselectionrequest.target == ATOM_SUN_SELECTION_END)) {
1326                 /* tell motif initiator we are done OLIT does not have a
1327                  * Failure; so all dnd exit status map to SUCCESS.
1328                  *
1329                  * Also record `requestor' id, so that we know who to
1330                  * forward when receiving SelectionNotify. */
1331             drop_info->receiver_win = event->xselectionrequest.requestor;
1332             DndDone(dpy, ATOM_MOTIF_SUCCESS, drop_info);
1333         }       
1334         else if (event->xselectionrequest.target == ATOM_MOTIF_SUCCESS
1335                  || (event->xselectionrequest.target == ATOM_MOTIF_FAILURE)) {
1336                 /* tell olit initiator we are done.
1337                  *
1338                  * We will have to remember this target value so that
1339                  * we know what to forward when receiving SelectionNotify.
1340                  *
1341                  * Note that olit protocol doesn't have a XmTRANSFER_FAILURE
1342                  * equivalence, the closest is _SUN_DRAGDROP_ERROR. We
1343                  * may want to consider it otherwise olit app will
1344                  * think the drop was successful...
1345                  */
1346             drop_info->status = event->xselectionrequest.target;
1347             DndDone(dpy, ATOM_SUN_DND_DONE, drop_info);
1348         }       
1349         else if (event->xselectionrequest.target == ATOM_SUN_SELECTION_ERROR)
1350         {
1351                 /* tell motif initiator OLIT can not handle this drop.
1352                  * we have a failure.
1353                  */
1354             DndDone(dpy, ATOM_MOTIF_FAILURE, drop_info);
1355         }
1356         else if (event->xselectionrequest.target == ATOM_MULTIPLE)
1357             ForwardMultpleSelectionRequest(
1358                                   &(event->xselectionrequest), drop_info);
1359         else {
1360             /* forward selection request to initiator */
1361             drop_info->source_win = event->xselectionrequest.requestor;
1362             XConvertSelection(event->xselectionrequest.display,
1363                                   drop_info->source_handle,
1364                                   event->xselectionrequest.target,
1365                                   event->xselectionrequest.property,
1366                                   drop_info->proxy_sel_req_win,
1367                                   event->xselectionrequest.time
1368                                   );
1369         }
1370         break;
1371     } /* end of case SelectionRequest */
1372
1373     case SelectionNotify:
1374 #ifdef DEBUG
1375         printf("SelectionNotify, sel=%s, target=%s, prop=%s, w=0x%lx, time=%ld\n",
1376                a2s(event->xselection.selection),
1377                a2s(event->xselection.target),
1378                a2s(event->xselection.property), event->xselection.requestor,
1379                event->xselection.time);
1380 #endif
1381     {
1382         drop_info_t *drop_info = GetDropInfo(event->xselection.requestor);
1383
1384         if (drop_info == NULL || 
1385             (event->xselection.time < drop_info->time_stamp))
1386             /* got SelectionNotify of unknown selection (source) */
1387             break;
1388
1389                 /* Trap these two targets, so that the destination side
1390                  * will not see selection-time-out */
1391         if (event->xselection.target == ATOM_SUN_DND_DONE ||  /* olit->motif */
1392             event->xselection.target == ATOM_MOTIF_SUCCESS) { /* motif->olit */
1393
1394                 XSelectionEvent * xe = &event->xselection;
1395
1396                         /* Currently, in both olit dnd protocol and
1397                          * motif dnd protocol, the destination side
1398                          * DON'T check the value from the source side!
1399                          *
1400                          * We will have to copy xe->property to
1401                          * drop_info->receiver_win if this is no longer
1402                          * TRUE in the future... */
1403                 xe->requestor = drop_info->receiver_win;
1404                 xe->selection = drop_info->proxy_handle;
1405                 xe->target = xe->target == ATOM_SUN_DND_DONE ?
1406                                 drop_info->status : ATOM_SUN_DND_DONE;
1407                 XSendEvent(xe->display, xe->requestor, False,
1408                                                 NoEventMask, event);
1409
1410                 ClearDropInfo(drop_info->proxy_handle);
1411                 break;
1412         }
1413         /* if event->property is proxy_handle then the convert request
1414            was from the proxy agent
1415            else the convert request was from the receiver */
1416         if (event->xselection.property == drop_info->proxy_handle ||
1417             (event->xselection.property == ATOM_SUN_DND_ACK)) {
1418             ProcessOlitInitiatorConversion(&event->xselection, drop_info);
1419         } else {
1420             if (ForwardConversion(&event->xselection, drop_info)) {
1421
1422                         /* We are here ONLY if the target is ATOM_MULTIPLE and
1423                          * the target list contains either _SUN_DRAGDROP_DONE or
1424                          * _SUN_SELECTION_END. Probably for XView support?! */
1425                 DndDone(dpy, ATOM_MOTIF_SUCCESS, drop_info);
1426             }
1427         }
1428         break;
1429     }   /* end of case SelectionNotify */
1430     }   /* end of switch */
1431 }
1432