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