Don't use fstat for readable pipe chars in dtexec.
[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 librararies 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()
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 Swap2Bytes(byte_order, data)
223     char byte_order;
224     CARD16 data;
225 {
226     static CARD16 ret_data;
227     char *old_p = (char *) &data;
228     char *new_p = (char *) &ret_data;
229
230     if (byte_order == motif_byte_order)
231         return data;
232
233     new_p[0] = old_p[1];
234     new_p[1] = old_p[0];
235     return ret_data;
236 }
237         
238 /* swap 4 bytes of data if the byte order is different */
239 Swap4Bytes(byte_order, data)
240     char byte_order;
241     CARD32 data;
242 {
243     static CARD32 ret_data;
244     char *old_p = (char *) &data;
245     char *new_p = (char *) &ret_data;
246
247     if (byte_order == motif_byte_order)
248         return data;
249
250     new_p[0] = old_p[3];
251     new_p[1] = old_p[2];
252     new_p[2] = old_p[1];
253     new_p[3] = old_p[0];
254     return ret_data;
255 }
256         
257
258 /* Get a new drop record */
259 /* use source_handle to indicate whether record is free or in use */
260 static drop_info_t *
261 NewDropInfo()
262 {
263     int i, oldest_index=0;
264
265     for(i=0; i<DROP_TABLE_MAX; i++)
266         if (drop_table[i].source_handle == 0) {
267             return drop_table+i;
268         }
269
270     /* if there are no more free records then use the oldest record */
271     for(i=1; i<DROP_TABLE_MAX; i++)
272         if (drop_table[i].time_stamp < drop_table[oldest_index].time_stamp)
273             oldest_index = i;
274
275     return drop_table+oldest_index;
276 }
277
278 /* Search for a drop record */
279 /* use source_handle as the key to the drop_info record */
280 static drop_info_t *
281 GetDropInfo(proxy_sel_req_win)
282     Window proxy_sel_req_win;
283 {
284     int i;
285
286     for(i=0; i<DROP_TABLE_MAX; i++)
287         if (drop_table[i].proxy_sel_req_win == proxy_sel_req_win) {
288             return drop_table+i;
289         }
290     return NULL;
291 }
292
293 /* Search for a drop record using the proxy_handle as the key */
294 static drop_info_t *
295 GetDropInfoUsingProxyHandle(proxy_handle)
296     Atom proxy_handle;
297 {
298     int i;
299
300     for(i=0; i<DROP_TABLE_MAX; i++)
301         if (drop_table[i].proxy_handle == proxy_handle
302             && drop_table[i].source_handle)
303             return drop_table+i;
304     return NULL;
305 }
306
307 /* Delete a drop record */
308 static
309 ClearDropInfo(proxy_handle)
310     Atom proxy_handle;
311 {
312     int i;
313     for(i=0; i<DROP_TABLE_MAX; i++)
314         if (drop_table[i].proxy_handle == proxy_handle) {
315             drop_table[i].source_handle = 0; /* reset flag */
316             break;
317         }
318 }
319             
320 /* get the window that has the atom associated with it */
321 Window 
322 GetAtomWindow(dpy, win, atom)
323         Display *dpy;
324         Window win;
325         Atom atom;
326 {
327     Window              root, parent;
328     Window              *children;
329     unsigned int        nchildren;
330     int                 i;
331     Atom                type = None;
332     int                 format;
333     unsigned long       nitems, after;
334     unsigned char       *data;
335     Window              inf = 0;
336
337     XGetWindowProperty(dpy, win, atom, 0, 0, False, AnyPropertyType,
338                        &type, &format, &nitems, &after, &data);
339     if (type)
340       return win;
341     else {
342         if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren) ||
343             (nchildren == 0))
344           return 0;
345         for (i = nchildren - 1; i >= 0; i--) {
346             if (inf = GetAtomWindow(dpy, children[i], atom))
347               return inf;
348         }
349     }
350     return 0;
351 }
352
353 /* get the window that is a property of the inputed window */
354 Window
355 GetPropertyWindow(dpy, in_win, atom)
356     Display *dpy;
357     Window in_win;
358     Atom atom;
359 {
360     Atom            type;
361     int             format;
362     unsigned long   lengthRtn;
363     unsigned long   bytesafter;
364     Window         *property = NULL;
365     Window          win = None;
366
367     if ((XGetWindowProperty (dpy,
368                              in_win,
369                              atom,
370                              0L, 1,
371                              False,
372                              AnyPropertyType,
373                              &type,
374                              &format,
375                              &lengthRtn,
376                              &bytesafter,
377                              (unsigned char **) &property) == Success) &&
378          (type == XA_WINDOW) &&
379          (format == 32) &&
380          (lengthRtn == 1)) {
381         win = *property;
382     }
383     if (property) {
384         XFree ((char *)property);
385     }
386
387     return (win);
388 }
389
390 /* startup initialization */
391 ProxyInit(dpy, dsdm_win)
392     Display *dpy;
393     Window dsdm_win;
394 {
395     enum { XA_MOTIF_DRAG_WINDOW, XA_MOTIF_DRAG_PROXY_WINDOW,
396            XA_MOTIF_DRAG_AND_DROP_MESSAGE,
397            XA_MOTIF_DRAG_INITIATOR_INFO, XA_MOTIF_DRAG_RECEIVER_INFO,
398            XAXmTRANSFER_SUCCESS, XAXmTRANSFER_FAILURE,
399            XA_MOTIF_DRAG_TARGETS, XA_SUN_DRAGDROP_TRIGGER,
400            XA_SUN_DRAGDROP_DONE, XA_SUN_SELECTION_END, XA_SUN_SELECTION_ERROR,
401            XA_SUN_DRAGDROP_ACK, XATARGETS, XAMULTIPLE,  NUM_ATOMS };
402     static char* atom_names[] = {
403            "_MOTIF_DRAG_WINDOW", "_MOTIF_DRAG_PROXY_WINDOW",
404            "_MOTIF_DRAG_AND_DROP_MESSAGE",
405            "_MOTIF_DRAG_INITIATOR_INFO", "_MOTIF_DRAG_RECEIVER_INFO",
406            "XmTRANSFER_SUCCESS", "XmTRANSFER_FAILURE",
407            "_MOTIF_DRAG_TARGETS", "_SUN_DRAGDROP_TRIGGER",
408            "_SUN_DRAGDROP_DONE", "_SUN_SELECTION_END", "_SUN_SELECTION_ERROR",
409            "_SUN_DRAGDROP_ACK", "TARGETS", "MULTIPLE" };
410
411     int i;
412     enum { ATOM_BUF_LEN = 25 };
413     char buf[ATOM_BUF_LEN * DROP_TABLE_MAX];
414     char *buf_names[DROP_TABLE_MAX];
415     Atom buf_atoms[DROP_TABLE_MAX];
416     XSetWindowAttributes attr;
417     Atom atoms[NUM_ATOMS];
418
419     InitializeByteOrder();
420
421     /* make motif_drag_win and proxy_win persistant */
422     XSetCloseDownMode(dpy, RetainPermanent);
423     XInternAtoms(dpy, atom_names, NUM_ATOMS, False, atoms);
424
425     ATOM_MOTIF_DRAG_WIN = atoms[XA_MOTIF_DRAG_WINDOW];
426     ATOM_MOTIF_PROXY_WIN = atoms[XA_MOTIF_DRAG_PROXY_WINDOW];
427     ATOM_MOTIF_DND_MESS = atoms[XA_MOTIF_DRAG_AND_DROP_MESSAGE];
428     ATOM_MOTIF_INITIATOR_INFO = atoms[XA_MOTIF_DRAG_INITIATOR_INFO];
429     ATOM_MOTIF_RECEIVER_INFO = atoms[XA_MOTIF_DRAG_RECEIVER_INFO];
430     ATOM_MOTIF_SUCCESS = atoms[XAXmTRANSFER_SUCCESS];
431     ATOM_MOTIF_FAILURE = atoms[XAXmTRANSFER_FAILURE];
432     ATOM_MOTIF_TARGETS = atoms[XA_MOTIF_DRAG_TARGETS];
433
434     ATOM_SUN_DND_TRIGGER = atoms[XA_SUN_DRAGDROP_TRIGGER];
435     ATOM_SUN_DND_DONE = atoms[XA_SUN_DRAGDROP_DONE];
436     ATOM_SUN_SELECTION_ERROR = atoms[XA_SUN_SELECTION_ERROR];
437     ATOM_SUN_SELECTION_END = atoms[XA_SUN_SELECTION_END];
438     ATOM_SUN_DND_ACK = atoms[XA_SUN_DRAGDROP_ACK];
439
440     ATOM_TARGETS = atoms[XATARGETS];
441     ATOM_MULTIPLE = atoms[XAMULTIPLE];
442
443     for (i=0; i<DROP_TABLE_MAX; i++) {
444         buf_names[i] = buf + (ATOM_BUF_LEN * i);
445         sprintf(buf_names[i], "DND_PROXY_HANDLE_%d", i);
446     }
447     XInternAtoms(dpy, buf_names, DROP_TABLE_MAX, False, buf_atoms);
448
449     for (i=0; i<DROP_TABLE_MAX; i++) {
450         drop_table[i].proxy_handle = buf_atoms[i];
451         drop_table[i].proxy_sel_req_win = 
452             XCreateWindow(dpy,
453                           DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0,
454                           InputOnly, CopyFromParent, 0, &attr);
455     }
456     motif_drag_win = GetPropertyWindow(dpy, DefaultRootWindow(dpy),
457                              ATOM_MOTIF_DRAG_WIN);
458
459     /* if motif_drag_win doesn't exist then define and declare it */
460     if (motif_drag_win == None) {
461         motif_drag_win = dsdm_win;
462         XChangeProperty (dpy,
463                      DefaultRootWindow(dpy),
464                      ATOM_MOTIF_DRAG_WIN,
465                      XA_WINDOW,
466                      32,
467                      PropModeReplace,
468                      (unsigned char *) &motif_drag_win,
469                      1);
470
471     }
472
473     /* define and declare the proxy_window */
474     proxy_win = dsdm_win;
475     XChangeProperty(dpy, motif_drag_win, 
476                     ATOM_MOTIF_PROXY_WIN, /* property */
477                     XA_WINDOW, /* type */
478                     32,               /* format */
479                     PropModeReplace, /* mode */
480                     (unsigned char *) &proxy_win, /* data */
481                     1
482                     );
483
484 #ifdef DEBUG
485     printf("drag_win=0x%lx, proxy_win=0x%lx\n", motif_drag_win, proxy_win);
486 #endif
487     /*  Watch for mapping of top level windows */
488     XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureNotifyMask);
489
490     /*  Watch for someone taking away proxy_win ownership */
491     XSelectInput(dpy, proxy_win, PropertyChangeMask);
492
493     for(i=0; i<DROP_TABLE_MAX; i++)
494         XSetSelectionOwner(dpy, drop_table[i].proxy_handle,
495                            proxy_win, CurrentTime);
496 }
497
498 /* Place the motif receiver property on the Openlook receiver's top
499    level window */
500 AdvertiseMotifDropSite(dpy, win)
501     Display *dpy;
502     Window win;
503 {
504     motif_receiver_t receiver_info;
505     unsigned long nitems;
506     CARD32 *data;
507     int motif_receiver = False;
508
509 #define OL_PREVIEW_INDEX 4
510 #define MOTIF_RECEIVER_FLAG     0x80000000
511     /* make sure receiver is not a Motif application */
512     data = (CARD32 *) GetInterestProperty(dpy, win, &nitems);
513     if (data != NULL) {
514         if (nitems > OL_PREVIEW_INDEX &&
515             (data[OL_PREVIEW_INDEX] & MOTIF_RECEIVER_FLAG))
516             motif_receiver = True;
517         XFree(data);
518     }
519     if (motif_receiver)
520         return;
521
522 #ifdef DEBUG
523     printf("AdvertiseMotifDropSite\n");
524 #endif
525
526     receiver_info.byte_order = motif_byte_order;
527     receiver_info.protocol_version = MOTIF_DRAG_PROTOCOL_VERSION;
528     receiver_info.drag_protocol_style = XmDRAG_DROP_ONLY;
529     receiver_info.proxy_window = proxy_win;
530     receiver_info.num_drop_sites = 0;  /* not used in drag_drop_only mode */
531     receiver_info.heap_offset = 0;     /* not used in drag_drop_only mode */
532
533     XChangeProperty(dpy,
534                     win,
535                     ATOM_MOTIF_RECEIVER_INFO, /* property */
536                     ATOM_MOTIF_RECEIVER_INFO, /* type */
537                     8,               /* format */
538                     PropModeReplace, /* mode */
539                     (unsigned char *) &receiver_info, /* data */
540                     sizeof(motif_receiver_t)  /* size of data in bytes */
541                     );
542 }
543
544 /* Determine if the drop coordinate is within a rectangle */
545 static char
546 InRect(drop_x, drop_y, x, y, width, height)
547     int drop_x, drop_y, x, y;
548     unsigned int width, height;
549 {
550     if ((drop_x >= x) && (drop_x <= (x+width)) &&
551         (drop_y >= y) && (drop_y <= (y+height)))
552         return True;
553     else
554         return False;
555 }
556
557 #define DRAGDROP_VERSION        0       /* OLIT dnd version */
558 #define INTEREST_RECT           0
559 #define INTEREST_WINDOW         1
560 #define NEXTWORD(result) if (index >= nitems)   {               \
561                              XFree(data);                       \
562                              return;                            \
563                          }                                      \
564                          (result) = data[index++]
565
566 /* get the drop site given the window and coordinate */
567 static void
568 GetOlitDropSite(dpy, top_level_win, drop_x, drop_y, ret_site_id, ret_event_win)
569     Display *dpy;
570             Window top_level_win;
571     INT16 drop_x, drop_y;
572     unsigned long *ret_site_id;
573     Window *ret_event_win;
574 {
575     unsigned long *data, nitems, version, nsites, event_win, site_id, flags;
576     unsigned long areatype, nrects, areawin;
577     int index = 0;
578     int i,j, junk;
579     char done = False;
580     Window wjunk;
581     int x,y;
582     unsigned int width, height, border, ujunk;
583     Window root_win;
584
585     *ret_event_win = *ret_site_id = 0;
586     data = (unsigned long *) GetInterestProperty(dpy, top_level_win, &nitems);
587     if (data == NULL)   return;
588
589     /* get root window */
590     if (XGetGeometry(dpy, top_level_win, &root_win, &junk, &junk,
591                      &ujunk, &ujunk, &ujunk, &ujunk) == 0)
592         return;
593
594     NEXTWORD(version);
595     if (version != DRAGDROP_VERSION)    return;
596     NEXTWORD(nsites);
597     for (i=0; i<nsites && !done; ++i) {
598         NEXTWORD(event_win);    NEXTWORD(site_id);      NEXTWORD(flags);
599         NEXTWORD(areatype);
600         switch (areatype) {
601         case INTEREST_RECT:
602             NEXTWORD(nrects);
603             for (j=0; j<nrects && !done; ++j) {
604                 NEXTWORD(x);
605                 NEXTWORD(y);
606                 NEXTWORD(width);
607                 NEXTWORD(height);
608                 if (XTranslateCoordinates(dpy, top_level_win, root_win, x, y,
609                                              &x, &y, &wjunk))
610                     done = InRect((int)drop_x,(int)drop_y, x, y, width,height);
611             }
612             break;
613         case INTEREST_WINDOW:
614             NEXTWORD(nrects);
615             for (j=0; j<nrects && !done; ++j) {
616                 NEXTWORD(areawin);
617                 if (0 == XGetGeometry(dpy, areawin, &wjunk, &junk, &junk,
618                                       &width, &height, &border, &ujunk)) {
619                     fprintf(stderr,
620                             "%s: XGetGeometry failed on window 0x%lx\n",
621                             ProgramName, areawin);
622                     return;
623                 }
624                 if (XTranslateCoordinates(dpy, areawin, root_win, 0, 0,
625                                              &x, &y, &wjunk)) {
626                     x = x - border;
627                     y = y - border;
628                     width = width + border;
629                     height = height + border;
630                     done = InRect((int)drop_x, (int)drop_y, x, y,width,height);
631                 }
632             }
633             break;
634         default:
635             fprintf(stderr,
636                     "%s: unknown site area type on window 0x%lx\n",
637                     ProgramName, top_level_win);
638             return;
639         }
640     }
641     if (done) {
642         *ret_site_id = site_id;
643         *ret_event_win = event_win;
644     }
645 }
646
647 #define MOTIF_DROP_MOVE_ACTION  (1 << 0)
648 #define MOTIF_DROP_COPY_ACTION  (1 << 1)
649 #define MOTIF_DROP_LINK_ACTION  (1 << 2)
650 #define MOTIF_DROP_ALLOWED_ACTIONS        0x0F00
651 #define OLIT_DROP_ACKNOWLEDGE_ACTION    (1 << 1)
652 #define OLIT_DROP_TRANSIENT_ACTION      (1 << 2)
653 #define OLIT_DROP_MOVE_ACTION   (1 << 0)
654 #define OLIT_DROP_COPY_ACTION   0
655 #define OLIT_DROP_LINK_ACTION   (1 << 10)
656
657 /* translate an OLIT operation to a Motif operation */
658 static CARD16
659 ConvertOlitAction(olit_action)
660     CARD32 olit_action;
661 {
662     CARD16 motif_action;
663
664     motif_action = MOTIF_DROP_COPY_ACTION | (MOTIF_DROP_COPY_ACTION << 8);
665
666     if (olit_action & OLIT_DROP_MOVE_ACTION)
667         motif_action |= MOTIF_DROP_MOVE_ACTION | (MOTIF_DROP_MOVE_ACTION << 8);
668     else if (olit_action & OLIT_DROP_LINK_ACTION)
669         motif_action |= MOTIF_DROP_LINK_ACTION | (MOTIF_DROP_LINK_ACTION << 8);
670
671     return motif_action;
672 }
673
674 /* translate a Motif operation to an OLIT operation */
675 static CARD32
676 ConvertMotifAction(motif_action)
677     CARD16 motif_action;
678 {
679     CARD32 olit_action;
680
681     olit_action = OLIT_DROP_TRANSIENT_ACTION;
682     if (motif_action & MOTIF_DROP_MOVE_ACTION)
683         olit_action |= OLIT_DROP_MOVE_ACTION;
684     else if (motif_action & MOTIF_DROP_COPY_ACTION)
685         olit_action |= OLIT_DROP_COPY_ACTION;
686     else if (motif_action & MOTIF_DROP_LINK_ACTION)
687         olit_action |= OLIT_DROP_LINK_ACTION;
688     else
689         olit_action = 0;
690     return olit_action;
691 }
692
693 /* Process Motif ClientMessage */
694 HandleMotifMessage(dpy, event)
695     Display *dpy;
696     XClientMessageEvent *event;
697 {
698     motif_drop_start_t info;
699     drop_info_t *drop_info;
700     Window dest_win;
701
702     dest_win = event->window;
703     info.message_type = event->data.b[0];
704     info.byte_order = event->data.b[1];
705
706     if (info.message_type & MOTIF_RECEIVER_BIT) {
707         /* receiver message
708            If initiator is Motif then forward it this message. */
709         if (event->window != proxy_win)
710             XSendEvent(dpy, event->window, False,
711                        NoEventMask, (XEvent *) event);
712     }
713     else {
714         /* initiator message */
715         if ((info.message_type & MOTIF_MESSAGE_TYPE_MASK)== MOTIF_DROP_START) {
716             unsigned long site_id;
717             Window event_win;
718             XClientMessageEvent out_event;
719             CARD32      olit_action;
720
721             /* receiver will never be MOTIF because MOTIF receivers
722                don't set the proxy field of RECEIVER_INFO atom
723                */
724
725             info.flags = Swap2Bytes(info.byte_order, event->data.s[1]);
726
727             /* since dropSiteStatus can't be determined in a short amount
728                of time, always return a valid drop site */
729             info.flags &= MOTIF_DROP_SITE_STATUS_MASK;
730             info.flags |= MOTIF_DROP_SITE_STATUS_VALID;
731
732             info.time = Swap4Bytes(info.byte_order, event->data.l[1]);
733             info.x = Swap2Bytes(info.byte_order, event->data.s[4]);
734             info.y = Swap2Bytes(info.byte_order, event->data.s[5]);
735             info.icc_handle = Swap4Bytes(info.byte_order, event->data.l[3]);
736             info.src_window = Swap4Bytes(info.byte_order, event->data.l[4]);
737
738             /* send a drop start ack here
739                to give ourselves more time to process the drop
740                because GetOlitDropSite or 
741                selection processing might take a long time
742                */
743             info.message_type = event->data.b[0] |= MOTIF_RECEIVER_BIT;
744             event->data.s[1] = Swap2Bytes(info.byte_order, info.flags);
745             event->window = info.src_window;
746             XSendEvent(dpy, event->window, False,
747                        NoEventMask, (XEvent *) event);
748
749             /* get olit drop site */
750             GetOlitDropSite(dpy, 
751                             dest_win, info.x, info.y, &site_id, &event_win);
752             drop_info = NewDropInfo();
753             olit_action = ConvertMotifAction(info.flags);
754             if (event_win && olit_action) {
755                 drop_info->source_handle = info.icc_handle;
756                 drop_info->time_stamp = info.time;
757
758                 out_event.type = ClientMessage;
759                 out_event.send_event = True;
760                 out_event.display = dpy;
761                 out_event.window = event_win;
762                 out_event.message_type = ATOM_SUN_DND_TRIGGER;
763                 out_event.format = 32;
764
765                 out_event.data.l[OLIT_DROP_SELECTION]= drop_info->proxy_handle;
766                 out_event.data.l[OLIT_DROP_TIME] = info.time;
767                 out_event.data.l[OLIT_DROP_COORDINATE] = 
768                     (info.x << 16) | info.y;
769                 out_event.data.l[OLIT_DROP_SITE_ID] = site_id;
770                 out_event.data.l[OLIT_DROP_OPERATION] = olit_action;
771                 XSendEvent(dpy, out_event.window, False,
772                        NoEventMask, (XEvent *) &out_event);
773             }
774             else {
775                 /* tell motif initiator that the drop failed */
776                 XConvertSelection(dpy,
777                                   info.icc_handle,
778                                   ATOM_MOTIF_FAILURE,
779                                   None,
780                                   proxy_win,
781                                   info.time);
782                 
783             }
784         }
785     }
786 }
787
788 /* Process OLIT ClientMessage */
789 static
790 HandleOlitTrigger(dpy, event)
791     Display *dpy;
792     XClientMessageEvent *event;
793 {
794     drop_site_t *site;
795     char success = False;
796     Atom source_handle = event->data.l[OLIT_DROP_SELECTION];
797     Time time_stamp = event->data.l[OLIT_DROP_TIME];
798
799     /* proxy agent only gets ATOM_SUN_DND_TRIGGER if
800        receiver is motif */
801     
802     /* map site_id to site_window */
803     for (site = MasterSiteList; site != NULL; site = site->next) {
804         if (site->site_id == event->data.l[OLIT_DROP_SITE_ID]) {
805             drop_info_t *drop_info = NewDropInfo();
806             
807             drop_info->motif_action = 
808                 ConvertOlitAction(event->data.l[OLIT_DROP_OPERATION]);
809             drop_info->receiver_win = site->window_id;
810             drop_info->source_handle = source_handle;
811             drop_info->trigger_x = (event->data.l[OLIT_DROP_COORDINATE]) >> 16;
812             drop_info->trigger_y = 
813                 (event->data.l[OLIT_DROP_COORDINATE]) & 0x0000ffff;
814             drop_info->time_stamp = time_stamp;
815
816             if ((event->data.l[OLIT_DROP_OPERATION])
817                 & OLIT_DROP_ACKNOWLEDGE_ACTION) {
818                 XConvertSelection(dpy,
819                               drop_info->source_handle, /* selection */
820                               ATOM_SUN_DND_ACK,
821                               ATOM_SUN_DND_ACK, /* property */
822                               drop_info->proxy_sel_req_win,
823                               drop_info->time_stamp /* time */
824                               );
825             }
826
827             /* get olit initiator targets */
828             XConvertSelection(dpy,
829                               drop_info->source_handle, /* selection */
830                               ATOM_TARGETS,
831                               drop_info->proxy_handle,  /* property */
832                               drop_info->proxy_sel_req_win,
833                               drop_info->time_stamp /* time */
834                               );
835             success = True;
836             break;
837         }
838     }               
839     if (!success)
840         /* tell olit initiator we are done */
841         XConvertSelection(dpy, source_handle,
842                           ATOM_SUN_DND_DONE,
843                           ATOM_SUN_DND_DONE, /* property */
844                           proxy_win,
845                           time_stamp);
846 }
847
848
849 /* Place the Motif initiator property on the initiator's top level window */
850 static void
851 UpdateInitiatorAtom(dpy, win, channel, targets_index)
852     Display *dpy;
853     Window win;
854     Atom channel;
855     CARD16      targets_index;
856 {
857     motif_initiator_t initiator_info;
858
859     initiator_info.byte_order = motif_byte_order;
860     initiator_info.protocol_version = MOTIF_DRAG_PROTOCOL_VERSION;
861     initiator_info.targets_index = targets_index;
862     initiator_info.icc_handle = channel;
863
864     XChangeProperty(dpy, win,
865                     channel, /* property */
866                     ATOM_MOTIF_INITIATOR_INFO, /* type */
867                     8,               /* format */
868                     PropModeReplace, /* mode */
869                     (unsigned char *) &initiator_info, /* data */
870                     sizeof(motif_initiator_t) /* size of data in bytes */
871                     );
872 }
873
874 /* Send Motif DROP_START ClientMessage to receiver */
875 SendStartDrop(dpy, src_win, drop_info)
876     Display *dpy;
877     Window src_win;
878     drop_info_t *drop_info;
879 {
880     motif_drop_start_t info;
881     XClientMessageEvent event;
882
883     info.message_type = MOTIF_DROP_START;
884     info.byte_order = motif_byte_order;
885     info.flags = drop_info->motif_action;
886     info.time = drop_info->time_stamp;
887     info.x = drop_info->trigger_x;
888     info.y = drop_info->trigger_y;
889     info.icc_handle = drop_info->proxy_handle;
890     info.src_window = src_win;
891
892     event.type = ClientMessage;
893     event.send_event = True;
894     event.display = dpy;
895     event.window = drop_info->receiver_win;
896     event.message_type = ATOM_MOTIF_DND_MESS;
897     event.format = 8;
898     memcpy(&event.data, &info, sizeof(info));
899     XSendEvent(dpy, event.window, False, NoEventMask, (XEvent *) &event);
900 }
901
902 /* Compare data of type Atom */
903 static int
904 AtomCompare(atom1, atom2)
905     Atom *atom1, *atom2;
906 {
907     return(*atom1 - *atom2);
908 }
909
910 /* Get the Motif TARGETS */
911 static void
912 GetTargetsList(dpy, targets_list_p)
913     Display *dpy;
914     motif_targets_t     **targets_list_p;
915 {
916     motif_targets_t     *targets_list;
917     Atom type;
918     int format, status;
919     unsigned long length, bytes_after;
920
921     status = XGetWindowProperty(dpy,
922                              motif_drag_win,
923                              ATOM_MOTIF_TARGETS,
924                              0L, GET_PROPERTY_MAX,
925                              False,
926                              ATOM_MOTIF_TARGETS,
927                              &type,
928                              &format,
929                              &length,
930                              &bytes_after,
931                              (unsigned char **) &targets_list);
932     if ((status != Success) || (type != ATOM_MOTIF_TARGETS) || (length == 0)) {
933         /* allocate targets_list of zero targets */
934         targets_list = (motif_targets_t *) malloc(sizeof(motif_targets_t));
935         targets_list->byte_order = motif_byte_order;
936         targets_list->protocol_version = MOTIF_DRAG_PROTOCOL_VERSION;
937         targets_list->num_target_lists = 0;
938         targets_list->size = sizeof(motif_targets_t);
939     }
940     *targets_list_p = targets_list;
941 }
942
943 static void
944 MatchTargets(targets_list, atoms, atom_cnt, targets_index_p)
945     motif_targets_t     *targets_list;
946     Atom                *atoms;
947     unsigned long       atom_cnt;
948     CARD16              *targets_index_p;
949 {
950     int target_index;
951     CARD32      *data;
952     int num_sub_list_items, sub_list_index;
953     CARD16      *num_sub_p;
954     char byte_order = targets_list->byte_order;
955     int num_target_lists;
956     CARD32 target, tmp_data;
957
958     num_target_lists = Swap2Bytes(byte_order, targets_list->num_target_lists);
959     data = (CARD32 *) (targets_list+1);
960     for (target_index=0; target_index < num_target_lists; target_index++) {
961         num_sub_p = (CARD16 *) data;
962         num_sub_list_items = (int) Swap2Bytes(byte_order, *num_sub_p);
963         data = (CARD32 *) (num_sub_p+1);
964
965         if (num_sub_list_items == atom_cnt) {
966             for (sub_list_index=0; sub_list_index < num_sub_list_items;
967                  sub_list_index++) {
968                /* address isn't long word aligned so copy one byte at a time */
969                 memcpy(&tmp_data, data+sub_list_index, 4);
970                 target = Swap4Bytes(byte_order, tmp_data);
971                 if (target != atoms[sub_list_index])
972                     break;
973             }
974             /* if all the atoms match then we got a match */
975             if (sub_list_index >= num_sub_list_items) {
976                 break;
977             }
978         }
979         data += num_sub_list_items;
980     }
981
982     *targets_index_p = target_index;
983 }
984
985 /* Put OLIT targets on the Motif TARGETS targets list */
986 static void
987 GetTargetsIndex(dpy, in_data, atom_cnt, targets_index_p)
988     Display             *dpy;
989     unsigned char *in_data;
990     unsigned long atom_cnt;
991     CARD16              *targets_index_p;  /* RETURN */
992 {
993     Atom                *atoms = (Atom *) in_data;
994     motif_targets_t     *new_list, *targets_list;
995     int                 i, old_size, new_size;
996     CARD16              *target_cnt;
997     CARD32              *target_data, tmp_data;
998     int                 malloc_length;
999     motif_initiator_t   initiator_info;
1000     char                byte_order;
1001     CARD16              num_target_lists;
1002
1003 #ifdef DEBUG
1004     for (i=0; i<atom_cnt; i++) {
1005         printf("GetTargetsIndex: target[%d]=%s\n", i, a2s(atoms[i]));
1006     }
1007 #endif
1008
1009     /* search existing targets list for a match, otherwise add our
1010        targets to the list */
1011     qsort((void *)atoms, (size_t)atom_cnt, (size_t)sizeof(Atom), AtomCompare);
1012     GetTargetsList(dpy, &targets_list);
1013     MatchTargets(targets_list, atoms, atom_cnt, targets_index_p);
1014     byte_order = targets_list->byte_order;
1015     num_target_lists = Swap2Bytes(byte_order,targets_list->num_target_lists);
1016     if (*targets_index_p < num_target_lists)
1017         return;
1018
1019     /* no match, add our targets to the list */
1020     old_size = Swap4Bytes(byte_order, targets_list->size);
1021     /* new size = old_list_size + target_cnt(CARD16) + targets(CARD32) */
1022     new_size = old_size + sizeof(CARD16) + (sizeof(CARD32) * atom_cnt);
1023     new_list = (motif_targets_t *) 
1024         realloc(targets_list, new_size);
1025
1026     /* change the new data's byte order to the existing data's byte order */
1027     /* put data in motif target list format */
1028     num_target_lists++;
1029     new_list->num_target_lists = Swap2Bytes(byte_order, num_target_lists);
1030     new_list->size = Swap4Bytes(byte_order, new_size);
1031
1032     target_cnt = (CARD16 *) (((BYTE *) new_list) + old_size);
1033     *target_cnt = Swap2Bytes(byte_order, atom_cnt); /* target_cnt */
1034     target_data = (CARD32 *) (target_cnt + 1);
1035     for (i=0; i<atom_cnt; i++) {
1036         /* address isn't long word aligned so copy one byte at a time */
1037         tmp_data = Swap4Bytes(byte_order, atoms[i]);
1038         memcpy(target_data+i, &tmp_data, 4);
1039     }
1040
1041     /* overwrite existing targets_list */
1042     XChangeProperty(dpy, motif_drag_win, ATOM_MOTIF_TARGETS,
1043                     ATOM_MOTIF_TARGETS, 8,
1044                     PropModeReplace,
1045                     (unsigned char *) new_list, new_size);
1046 }
1047
1048 /* 
1049   * update target list property
1050   * advertise psuedo motif initiator info property
1051   * send DROP_START message to motif receiver
1052   */
1053 static void
1054 ContinueHandleOlitTrigger(dpy, data, length, drop_info)
1055     Display *dpy;
1056     unsigned char *data;
1057     unsigned long length;
1058     drop_info_t         *drop_info;
1059 {
1060     CARD16              targets_index;
1061     Window psuedo_motif_initiator_win;
1062
1063     GetTargetsIndex(dpy, data, length, &targets_index);
1064     psuedo_motif_initiator_win = proxy_win;
1065     UpdateInitiatorAtom(dpy, psuedo_motif_initiator_win, 
1066                         drop_info->proxy_handle, targets_index);
1067     SendStartDrop(dpy, psuedo_motif_initiator_win, drop_info);
1068 }
1069
1070 /* Get OLIT initiator's TARGETS */
1071 ProcessOlitInitiatorConversion(event, drop_info)
1072     XSelectionEvent *event;
1073     drop_info_t         *drop_info;
1074 {
1075     Atom type;
1076     int format;
1077     unsigned long length, bytes_after;
1078     unsigned char *data;
1079
1080
1081     /* XView needs this */
1082     if (event->target == ATOM_SUN_DND_ACK)
1083         XChangeProperty(event->display, event->requestor, event->property,
1084                   event->target, 32, PropModeReplace, NULL, 0);
1085
1086     /* get OLIT targets
1087        sometimes type is TARGETS and sometimes type is ATOM so don't bother
1088        checking type */
1089     if ((event->target == ATOM_TARGETS) &&
1090         event->property && (XGetWindowProperty (event->display,
1091                              event->requestor,
1092                              event->property,
1093                              0L, GET_PROPERTY_MAX,
1094                              False,
1095                              AnyPropertyType,
1096                              &type,
1097                              &format,
1098                              &length,
1099                              &bytes_after,
1100                              (unsigned char **) &data) == Success)
1101         && length) {
1102
1103         ContinueHandleOlitTrigger(event->display, data, length, drop_info);
1104         XFree(data);
1105     }
1106 }
1107
1108 /* copy a property and the associated data from one window to another.
1109    Returns True if successful else returns False */
1110 static int
1111 CopyProperty(dpy, property, old_win, new_win)
1112     Display *dpy;
1113     Atom property;
1114     Window old_win, new_win;
1115 {
1116     Atom type;
1117     int format;
1118     unsigned long length, bytes_after;
1119     unsigned char *data;
1120
1121     if (property && (XGetWindowProperty (dpy, old_win, property,
1122                              0L, GET_PROPERTY_MAX,
1123                              False, 
1124                              AnyPropertyType,
1125                              &type,
1126                              &format,
1127                              &length,
1128                              &bytes_after,
1129                              (unsigned char **) &data) == Success)
1130         && format) {
1131         XChangeProperty(dpy, new_win, property,
1132                         type, format, PropModeReplace, data, (int) length);
1133         XFree(data);
1134         return True;
1135     }
1136     else
1137         return False;
1138 }
1139     
1140 /* copy the targets property and the associated data 
1141    from one window to another.
1142    Returns True if we get ATOM_SUN_DND_DONE or ATOM_SUN_SELECTION_END */
1143 static int
1144 CopyTargets(dpy, prop, old_win, new_win)
1145     Display *dpy;
1146     Atom prop;
1147     Window old_win, new_win;
1148 {
1149     Atom type;
1150     int format;
1151     unsigned long length, bytes_after;
1152     unsigned char *data;
1153     target_prop_t *target_info;
1154     int i;
1155     int ol_done = False;
1156
1157     if ((XGetWindowProperty (dpy, old_win, prop,
1158                              0L, GET_PROPERTY_MAX,
1159                              False, 
1160                              AnyPropertyType,
1161                              &type,
1162                              &format,
1163                              &length,
1164                              &bytes_after,
1165                              &data) == Success)
1166         && format) {
1167         target_info = (target_prop_t *) data;
1168         for(i=0; i < (length / 2); i++, target_info++) {
1169             DPRINTF(("CopyTargets: targets[%d]=%s\n", i, a2s(target_info->target)));
1170             CopyProperty(dpy, target_info->property, old_win, new_win);
1171             if (target_info->target == ATOM_SUN_DND_DONE ||
1172                 (target_info->target == ATOM_SUN_SELECTION_END))
1173                 ol_done = True;
1174         }
1175         XFree(data);
1176     }
1177     return ol_done;
1178 }
1179     
1180 /* Forward the SelectionNotify to the receiver.
1181    Returns True if we get ATOM_SUN_DND_DONE or ATOM_SUN_SELECTION_END */
1182 static
1183 ForwardConversion(event, drop_info)
1184     XSelectionEvent *event;
1185     drop_info_t         *drop_info;
1186 {
1187     int ol_done = False;
1188
1189     if (CopyProperty(event->display, event->property,
1190                      event->requestor, drop_info->source_win)) {
1191
1192         if (event->target == ATOM_MULTIPLE) {
1193             /* get target name and property pair
1194                copy all these properties */
1195             ol_done = CopyTargets(event->display, event->property,
1196                                   event->requestor, drop_info->source_win);
1197         }
1198     }
1199     else {
1200         /* initiator couldn't convert property, tell receiver this */
1201         event->property = 0;
1202     }
1203
1204     event->requestor = drop_info->source_win;
1205     event->selection = drop_info->proxy_handle;
1206     XSendEvent(event->display, event->requestor, False, 0, (XEvent*)event);
1207     return ol_done;
1208 }
1209
1210 /* This routine handles the selectionrequest for the target MULTIPLE.
1211    XtGetSelectionValues generates a selection request with the target
1212    MULTIPLE. This is how multiple targets selection
1213    (a selection request that asks for multiple targets) work:
1214    Window A calls XtGetSelectionValues
1215    Xt places a property on window A which contains all the targets.
1216    Xt asks selection owner of selection 1 to convert.
1217    Window B which is the selection owner of selection 1
1218        gets a selectionrequest.
1219    Window B reads the property on window A to get the targets
1220        and then converts them.
1221    Window B places the converted targets data in properties on window A.
1222    Window B places the target names and the properties holding
1223       the target data (target/property pair) in a property on window A.
1224
1225    With single target selection (XtGetSelectionValue), window B would
1226    get the target from the selectionrequest event structure and convert it.
1227
1228    The following is how the proxy agent behaves when there is a multiple
1229    targets selection:
1230    Window A calls XtGetSelectionValues
1231    Xt places a property on window A which contains all the targets.
1232    Xt asks selection owner of selection 1 to convert.
1233    Proxy agent which is the selection owner of selection 1
1234       gets a selectionrequest.
1235    Proxy agent reads the property on window A to get the targets.
1236    Proxy agent places a property on itself which contains all the targets.
1237    Proxy agent asks selection owner of selection 2 to convert.
1238    Window B which is the selection owner of selection 2 
1239       gets a selectionrequest.
1240    Window B reads the property on proxy agent to get the targets
1241    and then converts them.
1242    Window B places the converted targets data in properties on proxy agent.
1243    Window B places the target names and the properties holding
1244    the target data in a property proxy agent.
1245    Proxy agent reads the targets data and places the targets data
1246       in a property on window A.
1247    Proxy agent reads the target names and the properties holding
1248       the target data and places this info in a property on window A.
1249 */
1250 ForwardMultpleSelectionRequest(event, drop_info)
1251     XSelectionRequestEvent *event;
1252     drop_info_t *drop_info;
1253 {
1254     int format, status;
1255     unsigned long length, bytes_after;
1256     unsigned char *data;
1257
1258     drop_info->source_win = event->requestor;
1259
1260     if (CopyProperty(event->display, event->property, 
1261                      event->requestor, drop_info->proxy_sel_req_win)) {
1262
1263         /* change requestor and selection */
1264         event->requestor = drop_info->proxy_sel_req_win;
1265         event->selection = drop_info->source_handle;
1266
1267         /* forward selection request to initiator */
1268         XConvertSelection(event->display,
1269                           event->selection,
1270                           event->target,
1271                           event->property,
1272                           event->requestor,
1273                           event->time
1274                           );
1275     }
1276 }
1277
1278
1279 /* tell the initiator to clean up and do internal cleanup */
1280 DndDone(dpy, status, drop_info)
1281     Display *dpy;
1282     Atom status;
1283     drop_info_t *drop_info;
1284 {
1285     XConvertSelection(dpy,
1286                       drop_info->source_handle,
1287                       status,
1288                       drop_info->proxy_handle,
1289                       drop_info->proxy_sel_req_win,
1290                       drop_info->time_stamp);
1291
1292     /* XView needs this */
1293     if (status == ATOM_SUN_DND_DONE)
1294         XConvertSelection(dpy,
1295                       drop_info->source_handle,
1296                       ATOM_SUN_SELECTION_END,
1297                       drop_info->proxy_handle,
1298                       drop_info->proxy_sel_req_win,
1299                       drop_info->time_stamp);
1300 }
1301
1302
1303 ProxyMain(dpy, event)
1304     Display *dpy;
1305     XEvent *event;
1306 {
1307     switch(event->type) {
1308     case ClientMessage:
1309 #ifdef DEBUG
1310         printf("ClientMessage\n");
1311 #endif
1312         if (event->xclient.message_type == ATOM_SUN_DND_TRIGGER)
1313             HandleOlitTrigger(dpy, &event->xclient);
1314         else if (event->xclient.message_type == ATOM_MOTIF_DND_MESS)
1315             HandleMotifMessage(dpy, &event->xclient);
1316         break;
1317
1318     case MapNotify: {
1319         Window client_win;
1320
1321         client_win = GetAtomWindow(dpy, event->xcreatewindow.window,
1322                                      ATOM_WM_STATE);
1323         if (client_win) {
1324             /* if client has olit dnd interest then attach motif dnd
1325                interest to client */
1326             if (GetAtomWindow(dpy, client_win, ATOM_DRAGDROP_INTEREST)) {
1327                 AdvertiseMotifDropSite(dpy, client_win);
1328             }
1329             /* look for PropertyChange because olit receivers
1330                don't always declare ATOM_DRAGDROP_INTEREST at MapNotify time
1331                */
1332             XSelectInput(dpy, client_win, PropertyChangeMask);
1333         }
1334         break;
1335     }
1336
1337     case PropertyNotify:
1338 /*      printf("PropertyNotify, win=0x%lx\n", event->xproperty.window);*/
1339         /* if a OLIT drop site is advertised
1340            then advertise MOTIF drop site also */
1341         if (event->xproperty.atom == ATOM_DRAGDROP_INTEREST) {
1342             AdvertiseMotifDropSite(dpy, event->xproperty.window);
1343         }
1344         else if (event->xproperty.atom == ATOM_MOTIF_PROXY_WIN) {
1345             if ((motif_drag_win == event->xproperty.window) &&
1346                 (proxy_win != GetPropertyWindow(dpy, motif_drag_win,
1347                              ATOM_MOTIF_PROXY_WIN)))
1348                 /* proxy_win has been stolen by another client,
1349                    inform user */
1350
1351                 /* maybe the following should be a X message */
1352                 fprintf(stderr, "%s: lost proxy control\n", ProgramName);
1353         }
1354
1355         /* In the future, we might want to handle the case where
1356          * a client removes the ATOM_DRAGDROP_INTEREST or the
1357          * ATOM_MOTIF_RECEIVER_INFO atom
1358           */
1359         break;
1360
1361     case SelectionRequest: {
1362         drop_info_t *drop_info;
1363 #ifdef DEBUG
1364         printf("SelectionRequest, selection=%s, target=%s, prop=%s, win=0x%lx, time=%ld\n",
1365                    a2s(event->xselectionrequest.selection),
1366                    a2s(event->xselectionrequest.target),
1367                    a2s(event->xselectionrequest.property),
1368                    event->xselectionrequest.requestor,
1369                    event->xselectionrequest.time
1370                );
1371 #endif
1372         drop_info = GetDropInfoUsingProxyHandle
1373             (event->xselectionrequest.selection);
1374         if (drop_info == NULL)
1375             /* unknown selection, ignore the SelectionRequest */
1376             break;
1377         if (event->xselectionrequest.target == ATOM_SUN_DND_DONE ||
1378             (event->xselectionrequest.target == ATOM_SUN_SELECTION_END)) {
1379                 /* tell motif initiator we are done OLIT does not have a
1380                  * Failure; so all dnd exit status map to SUCCESS.
1381                  *
1382                  * Also record `requestor' id, so that we know who to
1383                  * forward when receiving SelectionNotify. */
1384             drop_info->receiver_win = event->xselectionrequest.requestor;
1385             DndDone(dpy, ATOM_MOTIF_SUCCESS, drop_info);
1386         }       
1387         else if (event->xselectionrequest.target == ATOM_MOTIF_SUCCESS
1388                  || (event->xselectionrequest.target == ATOM_MOTIF_FAILURE)) {
1389                 /* tell olit initiator we are done.
1390                  *
1391                  * We will have to remember this target value so that
1392                  * we know what to forward when receiving SelectionNotify.
1393                  *
1394                  * Note that olit protocol doesn't have a XmTRANSFER_FAILURE
1395                  * equivalence, the closest is _SUN_DRAGDROP_ERROR. We
1396                  * may want to consider it otherwise olit app will
1397                  * think the drop was successful...
1398                  */
1399             drop_info->status = event->xselectionrequest.target;
1400             DndDone(dpy, ATOM_SUN_DND_DONE, drop_info);
1401         }       
1402         else if (event->xselectionrequest.target == ATOM_SUN_SELECTION_ERROR)
1403         {
1404                 /* tell motif initiator OLIT can not handle this drop.
1405                  * we have a failure.
1406                  */
1407             DndDone(dpy, ATOM_MOTIF_FAILURE, drop_info);
1408         }
1409         else if (event->xselectionrequest.target == ATOM_MULTIPLE)
1410             ForwardMultpleSelectionRequest(
1411                                   &(event->xselectionrequest), drop_info);
1412         else {
1413             /* forward selection request to initiator */
1414             drop_info->source_win = event->xselectionrequest.requestor;
1415             XConvertSelection(event->xselectionrequest.display,
1416                                   drop_info->source_handle,
1417                                   event->xselectionrequest.target,
1418                                   event->xselectionrequest.property,
1419                                   drop_info->proxy_sel_req_win,
1420                                   event->xselectionrequest.time
1421                                   );
1422         }
1423         break;
1424     } /* end of case SelectionRequest */
1425
1426     case SelectionNotify:
1427 #ifdef DEBUG
1428         printf("SelectionNotify, sel=%s, target=%s, prop=%s, w=0x%lx, time=%ld\n",
1429                a2s(event->xselection.selection),
1430                a2s(event->xselection.target),
1431                a2s(event->xselection.property), event->xselection.requestor,
1432                event->xselection.time);
1433 #endif
1434     {
1435         drop_info_t *drop_info = GetDropInfo(event->xselection.requestor);
1436
1437         if (drop_info == NULL || 
1438             (event->xselection.time < drop_info->time_stamp))
1439             /* got SelectionNotify of unknown selection (source) */
1440             break;
1441
1442                 /* Trap these two targets, so that the destination side
1443                  * will not see selection-time-out */
1444         if (event->xselection.target == ATOM_SUN_DND_DONE ||  /* olit->motif */
1445             event->xselection.target == ATOM_MOTIF_SUCCESS) { /* motif->olit */
1446
1447                 XSelectionEvent * xe = &event->xselection;
1448
1449                         /* Currently, in both olit dnd protocol and
1450                          * motif dnd protocol, the destination side
1451                          * DONT check the value from the source side!
1452                          *
1453                          * We will have to copy xe->property to
1454                          * drop_info->receiver_win if this is no longer
1455                          * TRUE in the future... */
1456                 xe->requestor = drop_info->receiver_win;
1457                 xe->selection = drop_info->proxy_handle;
1458                 xe->target = xe->target == ATOM_SUN_DND_DONE ?
1459                                 drop_info->status : ATOM_SUN_DND_DONE;
1460                 XSendEvent(xe->display, xe->requestor, False,
1461                                                 NoEventMask, event);
1462
1463                 ClearDropInfo(drop_info->proxy_handle);
1464                 break;
1465         }
1466         /* if event->property is proxy_handle then the convert request
1467            was from the proxy agent
1468            else the convert request was from the receiver */
1469         if (event->xselection.property == drop_info->proxy_handle ||
1470             (event->xselection.property == ATOM_SUN_DND_ACK)) {
1471             ProcessOlitInitiatorConversion(&event->xselection, drop_info);
1472         } else {
1473             if (ForwardConversion(event, drop_info)) {
1474
1475                         /* We are here ONLY if the target is ATOM_MULTIPLE and
1476                          * the target list contains either _SUN_DRAGDROP_DONE or
1477                          * _SUN_SELECTION_END. Probably for XView support?! */
1478                 DndDone(dpy, ATOM_MOTIF_SUCCESS, drop_info);
1479             }
1480         }
1481         break;
1482     }   /* end of case SelectionNotify */
1483     }   /* end of switch */
1484 }