MotifDnDDropTargetProtocol.java revision 13050:98f89fe2b722
1/*
2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.awt.X11;
27
28import java.awt.Point;
29
30import java.awt.dnd.DnDConstants;
31
32import java.awt.event.MouseEvent;
33
34import java.io.IOException;
35
36import jdk.internal.misc.Unsafe;
37
38/**
39 * XDropTargetProtocol implementation for Motif DnD protocol.
40 *
41 * @since 1.5
42 */
43class MotifDnDDropTargetProtocol extends XDropTargetProtocol {
44    private static final Unsafe unsafe = XlibWrapper.unsafe;
45
46    private long sourceWindow = 0;
47    private long sourceWindowMask = 0;
48    private int sourceProtocolVersion = 0;
49    private int sourceActions = DnDConstants.ACTION_NONE;
50    private long[] sourceFormats = null;
51    private long sourceAtom = 0;
52    private int userAction = DnDConstants.ACTION_NONE;
53    private int sourceX = 0;
54    private int sourceY = 0;
55    private XWindow targetXWindow = null;
56    private boolean topLevelLeavePostponed = false;
57
58    protected MotifDnDDropTargetProtocol(XDropTargetProtocolListener listener) {
59        super(listener);
60    }
61
62    /**
63     * Creates an instance associated with the specified listener.
64     *
65     * @throws NullPointerException if listener is <code>null</code>.
66     */
67    static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
68        return new MotifDnDDropTargetProtocol(listener);
69    }
70
71    public String getProtocolName() {
72        return XDragAndDropProtocols.MotifDnD;
73    }
74
75    public void registerDropTarget(long window) {
76        assert XToolkit.isAWTLockHeldByCurrentThread();
77
78        MotifDnDConstants.writeDragReceiverInfoStruct(window);
79    }
80
81    public void unregisterDropTarget(long window) {
82        assert XToolkit.isAWTLockHeldByCurrentThread();
83
84        MotifDnDConstants.XA_MOTIF_ATOM_0.DeleteProperty(window);
85    }
86
87    public void registerEmbedderDropSite(long embedder) {
88        assert XToolkit.isAWTLockHeldByCurrentThread();
89
90        boolean overriden = false;
91        int version = 0;
92        long proxy = 0;
93        long newProxy = XDropTargetRegistry.getDnDProxyWindow();
94        int status = 0;
95        long data = 0;
96        int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE;
97
98        WindowPropertyGetter wpg =
99            new WindowPropertyGetter(embedder,
100                                     MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
101                                     0, 0xFFFF, false,
102                                     XConstants.AnyPropertyType);
103
104        try {
105            status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
106
107            /*
108             * DragICCI.h:
109             *
110             * typedef struct _xmDragReceiverInfoStruct{
111             *     BYTE byte_order;
112             *     BYTE protocol_version;
113             *     BYTE drag_protocol_style;
114             *     BYTE pad1;
115             *     CARD32       proxy_window B32;
116             *     CARD16       num_drop_sites B16;
117             *     CARD16       pad2 B16;
118             *     CARD32       heap_offset B32;
119             * } xmDragReceiverInfoStruct;
120             */
121            if (status == XConstants.Success && wpg.getData() != 0 &&
122                wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
123                wpg.getNumberOfItems() >=
124                MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
125
126                overriden = true;
127                data = wpg.getData();
128                dataSize = wpg.getNumberOfItems();
129
130                byte byteOrderByte = unsafe.getByte(data);
131
132                {
133                    int tproxy = unsafe.getInt(data + 4);
134                    if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
135                        tproxy = MotifDnDConstants.Swapper.swap(tproxy);
136                    }
137                    proxy = tproxy;
138                }
139
140                if (proxy == newProxy) {
141                    // Embedder already registered.
142                    return;
143                }
144
145                {
146                    int tproxy = (int)newProxy;
147                    if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
148                        tproxy = MotifDnDConstants.Swapper.swap(tproxy);
149                    }
150                    unsafe.putInt(data + 4, tproxy);
151                }
152            } else {
153                data = unsafe.allocateMemory(dataSize);
154
155                unsafe.putByte(data, MotifDnDConstants.getByteOrderByte()); /* byte order */
156                unsafe.putByte(data + 1, MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION); /* protocol version */
157                unsafe.putByte(data + 2, (byte)MotifDnDConstants.MOTIF_DYNAMIC_STYLE); /* protocol style */
158                unsafe.putByte(data + 3, (byte)0); /* pad */
159                unsafe.putInt(data + 4, (int)newProxy); /* proxy window */
160                unsafe.putShort(data + 8, (short)0); /* num_drop_sites */
161                unsafe.putShort(data + 10, (short)0); /* pad */
162                unsafe.putInt(data + 12, dataSize);
163            }
164
165            XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
166            XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder,
167                                        MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
168                                        MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
169                                        8, XConstants.PropModeReplace,
170                                        data, dataSize);
171            XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
172
173            if ((XErrorHandlerUtil.saved_error != null) &&
174                (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
175                throw new XException("Cannot write Motif receiver info property");
176            }
177        } finally {
178            if (!overriden) {
179                unsafe.freeMemory(data);
180                data = 0;
181            }
182            wpg.dispose();
183        }
184
185        putEmbedderRegistryEntry(embedder, overriden, version, proxy);
186    }
187
188    public void unregisterEmbedderDropSite(long embedder) {
189        assert XToolkit.isAWTLockHeldByCurrentThread();
190
191        EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder);
192
193        if (entry == null) {
194            return;
195        }
196
197        if (entry.isOverriden()) {
198            int status = 0;
199
200            WindowPropertyGetter wpg =
201                new WindowPropertyGetter(embedder,
202                                         MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
203                                         0, 0xFFFF, false,
204                                         XConstants.AnyPropertyType);
205
206            try {
207                status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
208
209                /*
210                 * DragICCI.h:
211                 *
212                 * typedef struct _xmDragReceiverInfoStruct{
213                 *     BYTE     byte_order;
214                 *     BYTE     protocol_version;
215                 *     BYTE     drag_protocol_style;
216                 *     BYTE     pad1;
217                 *     CARD32   proxy_window B32;
218                 *     CARD16   num_drop_sites B16;
219                 *     CARD16   pad2 B16;
220                 *     CARD32   heap_offset B32;
221                 * } xmDragReceiverInfoStruct;
222                 */
223                if (status == XConstants.Success && wpg.getData() != 0 &&
224                    wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
225                    wpg.getNumberOfItems() >=
226                    MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
227
228                    int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE;
229                    long data = wpg.getData();
230                    byte byteOrderByte = unsafe.getByte(data);
231
232                    int tproxy = (int)entry.getProxy();
233                    if (MotifDnDConstants.getByteOrderByte() != byteOrderByte) {
234                        tproxy = MotifDnDConstants.Swapper.swap(tproxy);
235                    }
236
237                    unsafe.putInt(data + 4, tproxy);
238
239                    XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
240                    XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder,
241                                                MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
242                                                MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
243                                                8, XConstants.PropModeReplace,
244                                                data, dataSize);
245                    XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
246
247                    if ((XErrorHandlerUtil.saved_error != null) &&
248                        (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
249                        throw new XException("Cannot write Motif receiver info property");
250                    }
251                }
252            } finally {
253                wpg.dispose();
254            }
255        } else {
256            MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.DeleteProperty(embedder);
257        }
258    }
259
260    /*
261     * Gets and stores in the registry the embedder's Motif DnD drop site info
262     * from the embedded.
263     */
264    public void registerEmbeddedDropSite(long embedded) {
265        assert XToolkit.isAWTLockHeldByCurrentThread();
266
267        boolean overriden = false;
268        int version = 0;
269        long proxy = 0;
270        int status = 0;
271
272        WindowPropertyGetter wpg =
273            new WindowPropertyGetter(embedded,
274                                     MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
275                                     0, 0xFFFF, false,
276                                     XConstants.AnyPropertyType);
277
278        try {
279            status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
280
281            /*
282             * DragICCI.h:
283             *
284             * typedef struct _xmDragReceiverInfoStruct{
285             *     BYTE byte_order;
286             *     BYTE protocol_version;
287             *     BYTE drag_protocol_style;
288             *     BYTE pad1;
289             *     CARD32       proxy_window B32;
290             *     CARD16       num_drop_sites B16;
291             *     CARD16       pad2 B16;
292             *     CARD32       heap_offset B32;
293             * } xmDragReceiverInfoStruct;
294             */
295            if (status == XConstants.Success && wpg.getData() != 0 &&
296                wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
297                wpg.getNumberOfItems() >=
298                MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
299
300                overriden = true;
301                long data = wpg.getData();
302
303                byte byteOrderByte = unsafe.getByte(data);
304
305                {
306                    int tproxy = unsafe.getInt(data + 4);
307                    if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
308                        tproxy = MotifDnDConstants.Swapper.swap(tproxy);
309                    }
310                    proxy = tproxy;
311                }
312            }
313        } finally {
314            wpg.dispose();
315        }
316
317        putEmbedderRegistryEntry(embedded, overriden, version, proxy);
318    }
319
320    public boolean isProtocolSupported(long window) {
321        WindowPropertyGetter wpg =
322            new WindowPropertyGetter(window,
323                                     MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
324                                     0, 0xFFFF, false,
325                                     XConstants.AnyPropertyType);
326
327        try {
328            int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
329
330            if (status == XConstants.Success && wpg.getData() != 0 &&
331                wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
332                wpg.getNumberOfItems() >=
333                MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
334                return true;
335            } else {
336                return false;
337            }
338        } finally {
339            wpg.dispose();
340        }
341    }
342
343    private boolean processTopLevelEnter(XClientMessageEvent xclient) {
344        assert XToolkit.isAWTLockHeldByCurrentThread();
345
346        if (targetXWindow != null || sourceWindow != 0) {
347            return false;
348        }
349
350        if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow)
351            && getEmbedderRegistryEntry(xclient.get_window()) == null) {
352            return false;
353        }
354
355        long source_win = 0;
356        long source_win_mask = 0;
357        int protocol_version = 0;
358        long property_atom = 0;
359        long[] formats = null;
360
361        {
362            long data = xclient.get_data();
363            byte eventByteOrder = unsafe.getByte(data + 1);
364            source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder);
365            property_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
366        }
367
368        /* Extract the available data types. */
369        {
370            WindowPropertyGetter wpg =
371                new WindowPropertyGetter(source_win,
372                                         XAtom.get(property_atom),
373                                         0, 0xFFFF,
374                                         false,
375                                         MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom());
376
377            try {
378                int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
379
380                if (status == XConstants.Success && wpg.getData() != 0 &&
381                    wpg.getActualType() ==
382                    MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom() &&
383                    wpg.getActualFormat() == 8 &&
384                    wpg.getNumberOfItems() ==
385                    MotifDnDConstants.MOTIF_INITIATOR_INFO_SIZE) {
386
387                    long data = wpg.getData();
388                    byte propertyByteOrder = unsafe.getByte(data);
389
390                    protocol_version = unsafe.getByte(data + 1);
391
392                    if (protocol_version !=
393                        MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION) {
394                        return false;
395                    }
396
397                    int index =
398                        MotifDnDConstants.Swapper.getShort(data + 2, propertyByteOrder);
399
400                    formats = MotifDnDConstants.getTargetListForIndex(index);
401                } else {
402                    formats = new long[0];
403                }
404            } finally {
405                wpg.dispose();
406            }
407        }
408
409        /*
410         * Select for StructureNotifyMask to receive DestroyNotify in case of source
411         * crash.
412         */
413        XWindowAttributes wattr = new XWindowAttributes();
414        try {
415            XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
416            int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
417                                                          source_win, wattr.pData);
418
419            XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
420
421            if ((status == 0) ||
422                ((XErrorHandlerUtil.saved_error != null) &&
423                (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
424                throw new XException("XGetWindowAttributes failed");
425            }
426
427            source_win_mask = wattr.get_your_event_mask();
428        } finally {
429            wattr.dispose();
430        }
431
432        XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
433        XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win,
434                                 source_win_mask |
435                                 XConstants.StructureNotifyMask);
436
437        XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
438
439        if ((XErrorHandlerUtil.saved_error != null) &&
440            (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
441            throw new XException("XSelectInput failed");
442        }
443
444        sourceWindow = source_win;
445        sourceWindowMask = source_win_mask;
446        sourceProtocolVersion = protocol_version;
447        /*
448         * TOP_LEVEL_ENTER doesn't communicate the list of supported actions
449         * They are provided in DRAG_MOTION.
450         */
451        sourceActions = DnDConstants.ACTION_NONE;
452        sourceFormats = formats;
453        sourceAtom = property_atom;
454
455        return true;
456    }
457
458    private boolean processDragMotion(XClientMessageEvent xclient) {
459        long data = xclient.get_data();
460        byte eventByteOrder = unsafe.getByte(data + 1);
461        byte eventReason = (byte)(unsafe.getByte(data) &
462                                  MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
463        int x = 0;
464        int y = 0;
465
466        short flags = MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder);
467
468        int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >>
469            MotifDnDConstants.MOTIF_DND_ACTION_SHIFT;
470        int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >>
471            MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT;
472
473        int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action);
474        int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions);
475
476        /* Append source window id to the event data, so that we can send the
477           response properly. */
478        {
479            int win = (int)sourceWindow;
480            if (eventByteOrder != MotifDnDConstants.getByteOrderByte()) {
481                win = MotifDnDConstants.Swapper.swap(win);
482            }
483            unsafe.putInt(data + 12, win);
484        }
485
486        XWindow xwindow = null;
487        {
488            XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
489            if (xbasewindow instanceof XWindow) {
490                xwindow = (XWindow)xbasewindow;
491            }
492        }
493
494        if (eventReason == MotifDnDConstants.OPERATION_CHANGED) {
495            /* OPERATION_CHANGED event doesn't provide coordinates, so we use
496               previously stored position and component ref. */
497            x = sourceX;
498            y = sourceY;
499
500            if (xwindow == null) {
501                xwindow = targetXWindow;
502            }
503        } else {
504            x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder);
505            y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder);
506
507            if (xwindow == null) {
508                long receiver =
509                    XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
510                        xclient.get_window(), x, y);
511
512                if (receiver != 0) {
513                    XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
514                    if (xbasewindow instanceof XWindow) {
515                        xwindow = (XWindow)xbasewindow;
516                    }
517                }
518            }
519
520            if (xwindow != null) {
521                Point p = xwindow.toLocal(x, y);
522                x = p.x;
523                y = p.y;
524            }
525        }
526
527        if (xwindow == null) {
528            if (targetXWindow != null) {
529                notifyProtocolListener(targetXWindow, x, y,
530                                       DnDConstants.ACTION_NONE, java_actions,
531                                       xclient, MouseEvent.MOUSE_EXITED);
532            }
533        } else {
534            int java_event_id = 0;
535
536            if (targetXWindow == null) {
537                java_event_id = MouseEvent.MOUSE_ENTERED;
538            } else {
539                java_event_id = MouseEvent.MOUSE_DRAGGED;
540            }
541
542            notifyProtocolListener(xwindow, x, y, java_action, java_actions,
543                                   xclient, java_event_id);
544        }
545
546        sourceActions = java_actions;
547        userAction = java_action;
548        sourceX = x;
549        sourceY = y;
550        targetXWindow = xwindow;
551
552        return true;
553    }
554
555    private boolean processTopLevelLeave(XClientMessageEvent xclient) {
556        assert XToolkit.isAWTLockHeldByCurrentThread();
557
558        long data = xclient.get_data();
559        byte eventByteOrder = unsafe.getByte(data + 1);
560
561        long source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder);
562
563        /* Ignore Motif DnD messages from all other windows. */
564        if (source_win != sourceWindow) {
565            return false;
566        }
567
568        /*
569         * Postpone upcall to java, so that we can abort it in case
570         * if drop immediatelly follows (see BugTraq ID 4395290).
571         * Send a dummy ClientMessage event to guarantee that a postponed java
572         * upcall will be processed.
573         */
574        topLevelLeavePostponed = true;
575        {
576            long proxy;
577
578            /*
579             * If this is an embedded drop site, the event should go to the
580             * awt_root_window as this is a proxy for all embedded drop sites.
581             * Otherwise the event should go to the event->window, as we don't use
582             * proxies for normal drop sites.
583             */
584            if (getEmbedderRegistryEntry(xclient.get_window()) != null) {
585                proxy = XDropTargetRegistry.getDnDProxyWindow();
586            } else {
587                proxy = xclient.get_window();
588            }
589
590            XClientMessageEvent dummy = new XClientMessageEvent();
591
592            try {
593                dummy.set_type(XConstants.ClientMessage);
594                dummy.set_window(xclient.get_window());
595                dummy.set_format(32);
596                dummy.set_message_type(0);
597                dummy.set_data(0, 0);
598                dummy.set_data(1, 0);
599                dummy.set_data(2, 0);
600                dummy.set_data(3, 0);
601                dummy.set_data(4, 0);
602                XlibWrapper.XSendEvent(XToolkit.getDisplay(),
603                                       proxy, false, XConstants.NoEventMask,
604                                       dummy.pData);
605            } finally {
606                dummy.dispose();
607            }
608        }
609        return true;
610    }
611
612    private boolean processDropStart(XClientMessageEvent xclient) {
613        long data = xclient.get_data();
614        byte eventByteOrder = unsafe.getByte(data + 1);
615
616        long source_win =
617            MotifDnDConstants.Swapper.getInt(data + 16, eventByteOrder);
618
619        /* Ignore Motif DnD messages from all other windows. */
620        if (source_win != sourceWindow) {
621            return false;
622        }
623
624        long property_atom =
625            MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
626
627        short flags =
628            MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder);
629
630        int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >>
631            MotifDnDConstants.MOTIF_DND_ACTION_SHIFT;
632        int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >>
633            MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT;
634
635        int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action);
636        int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions);
637
638        int x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder);
639        int y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder);
640
641        XWindow xwindow = null;
642        {
643            XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
644            if (xbasewindow instanceof XWindow) {
645                xwindow = (XWindow)xbasewindow;
646            }
647        }
648
649        if (xwindow == null) {
650            long receiver =
651                XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
652                    xclient.get_window(), x, y);
653
654            if (receiver != 0) {
655                XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
656                if (xbasewindow instanceof XWindow) {
657                    xwindow = (XWindow)xbasewindow;
658                }
659            }
660        }
661
662        if (xwindow != null) {
663            Point p = xwindow.toLocal(x, y);
664            x = p.x;
665            y = p.y;
666        }
667
668        if (xwindow != null) {
669            notifyProtocolListener(xwindow, x, y, java_action, java_actions,
670                                   xclient, MouseEvent.MOUSE_RELEASED);
671        } else if (targetXWindow != null) {
672            notifyProtocolListener(targetXWindow, x, y,
673                                   DnDConstants.ACTION_NONE, java_actions,
674                                   xclient, MouseEvent.MOUSE_EXITED);
675        }
676
677        return true;
678    }
679
680    public int getMessageType(XClientMessageEvent xclient) {
681        if (xclient.get_message_type() !=
682            MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
683
684            return UNKNOWN_MESSAGE;
685        }
686
687        long data = xclient.get_data();
688        byte reason = (byte)(unsafe.getByte(data) &
689                             MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
690
691        switch (reason) {
692        case MotifDnDConstants.TOP_LEVEL_ENTER :
693            return ENTER_MESSAGE;
694        case MotifDnDConstants.DRAG_MOTION :
695        case MotifDnDConstants.OPERATION_CHANGED :
696            return MOTION_MESSAGE;
697        case MotifDnDConstants.TOP_LEVEL_LEAVE :
698            return LEAVE_MESSAGE;
699        case MotifDnDConstants.DROP_START :
700            return DROP_MESSAGE;
701        default:
702            return UNKNOWN_MESSAGE;
703        }
704    }
705
706    protected boolean processClientMessageImpl(XClientMessageEvent xclient) {
707        if (xclient.get_message_type() !=
708            MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
709            if (topLevelLeavePostponed) {
710                topLevelLeavePostponed = false;
711                cleanup();
712            }
713
714            return false;
715        }
716
717        long data = xclient.get_data();
718        byte reason = (byte)(unsafe.getByte(data) &
719            MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
720        byte origin = (byte)(unsafe.getByte(data) &
721            MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
722
723        if (topLevelLeavePostponed) {
724            topLevelLeavePostponed = false;
725            if (reason != MotifDnDConstants.DROP_START) {
726                cleanup();
727            }
728        }
729
730        /* Only initiator messages should be handled. */
731        if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
732            return false;
733        }
734
735        switch (reason) {
736        case MotifDnDConstants.TOP_LEVEL_ENTER :
737            return processTopLevelEnter(xclient);
738        case MotifDnDConstants.DRAG_MOTION :
739        case MotifDnDConstants.OPERATION_CHANGED :
740            return processDragMotion(xclient);
741        case MotifDnDConstants.TOP_LEVEL_LEAVE :
742            return processTopLevelLeave(xclient);
743        case MotifDnDConstants.DROP_START :
744            return processDropStart(xclient);
745        default:
746            return false;
747        }
748    }
749
750    /*
751     * Currently we don't synthesize enter/leave messages for Motif DnD
752     * protocol. See comments in XDropTargetProtocol.postProcessClientMessage.
753     */
754    protected void sendEnterMessageToToplevel(long win,
755                                              XClientMessageEvent xclient) {
756        throw new Error("UNIMPLEMENTED");
757    }
758
759    protected void sendLeaveMessageToToplevel(long win,
760                                              XClientMessageEvent xclient) {
761        throw new Error("UNIMPLEMENTED");
762    }
763
764    public boolean forwardEventToEmbedded(long embedded, long ctxt,
765                                          int eventID) {
766        // UNIMPLEMENTED.
767        return false;
768    }
769
770    public boolean isXEmbedSupported() {
771        return false;
772    }
773
774    public boolean sendResponse(long ctxt, int eventID, int action) {
775        XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
776        if (xclient.get_message_type() !=
777            MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
778            return false;
779        }
780
781        long data = xclient.get_data();
782        byte reason = (byte)(unsafe.getByte(data) &
783            MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
784        byte origin = (byte)(unsafe.getByte(data) &
785            MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
786        byte eventByteOrder = unsafe.getByte(data + 1);
787        byte response_reason = (byte)0;
788
789        /* Only initiator messages should be handled. */
790        if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
791            return false;
792        }
793
794        switch (reason) {
795        case MotifDnDConstants.TOP_LEVEL_ENTER:
796        case MotifDnDConstants.TOP_LEVEL_LEAVE:
797            /* Receiver shouldn't rely to these messages. */
798            return false;
799        case MotifDnDConstants.DRAG_MOTION:
800            switch (eventID) {
801            case MouseEvent.MOUSE_ENTERED:
802                response_reason = MotifDnDConstants.DROP_SITE_ENTER;
803                break;
804            case MouseEvent.MOUSE_DRAGGED:
805                response_reason = MotifDnDConstants.DRAG_MOTION;
806                break;
807            case MouseEvent.MOUSE_EXITED:
808                response_reason = MotifDnDConstants.DROP_SITE_LEAVE;
809                break;
810            }
811            break;
812        case MotifDnDConstants.OPERATION_CHANGED:
813        case MotifDnDConstants.DROP_START:
814            response_reason = reason;
815            break;
816        default:
817            // Unknown reason. Shouldn't get here.
818            assert false;
819        }
820
821        XClientMessageEvent msg = new XClientMessageEvent();
822
823        try {
824            msg.set_type(XConstants.ClientMessage);
825            msg.set_window(MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder));
826            msg.set_format(8);
827            msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
828
829            long responseData = msg.get_data();
830
831            unsafe.putByte(responseData, (byte)(response_reason |
832                           MotifDnDConstants.MOTIF_MESSAGE_FROM_RECEIVER));
833            unsafe.putByte(responseData + 1, MotifDnDConstants.getByteOrderByte());
834
835            int response_flags = 0;
836
837            if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) {
838                short flags = MotifDnDConstants.Swapper.getShort(data + 2,
839                                                                 eventByteOrder);
840                byte dropSiteStatus = (action == DnDConstants.ACTION_NONE) ?
841                    MotifDnDConstants.MOTIF_INVALID_DROP_SITE :
842                    MotifDnDConstants.MOTIF_VALID_DROP_SITE;
843
844                /* Clear action and drop site status bits. */
845                response_flags = flags &
846                    ~MotifDnDConstants.MOTIF_DND_ACTION_MASK &
847                    ~MotifDnDConstants.MOTIF_DND_STATUS_MASK;
848                /* Fill in new action and drop site status. */
849                response_flags |=
850                    MotifDnDConstants.getMotifActionsForJavaActions(action) <<
851                    MotifDnDConstants.MOTIF_DND_ACTION_SHIFT;
852                response_flags |=
853                    dropSiteStatus << MotifDnDConstants.MOTIF_DND_STATUS_SHIFT;
854            } else {
855                response_flags = 0;
856            }
857
858            unsafe.putShort(responseData + 2, (short)response_flags);
859
860            /* Write time stamp. */
861            int time = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder);
862            unsafe.putInt(responseData + 4, time);
863
864            /* Write coordinates. */
865            if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) {
866                short x = MotifDnDConstants.Swapper.getShort(data + 8,
867                                                             eventByteOrder);
868                short y = MotifDnDConstants.Swapper.getShort(data + 10,
869                                                             eventByteOrder);
870                unsafe.putShort(responseData + 8, x); // x
871                unsafe.putShort(responseData + 10, y); // y
872            } else {
873                unsafe.putShort(responseData + 8, (short)0); // x
874                unsafe.putShort(responseData + 10, (short)0); // y
875            }
876
877            XToolkit.awtLock();
878            try {
879                XlibWrapper.XSendEvent(XToolkit.getDisplay(),
880                                       msg.get_window(),
881                                       false, XConstants.NoEventMask,
882                                       msg.pData);
883            } finally {
884                XToolkit.awtUnlock();
885            }
886        } finally {
887            msg.dispose();
888        }
889
890        return true;
891    }
892
893    public Object getData(long ctxt, long format)
894      throws IllegalArgumentException, IOException {
895        XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
896
897        if (xclient.get_message_type() !=
898            MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
899            throw new IllegalArgumentException();
900        }
901
902        long data = xclient.get_data();
903        byte reason = (byte)(unsafe.getByte(data) &
904            MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
905        byte origin = (byte)(unsafe.getByte(data) &
906            MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
907        byte eventByteOrder = unsafe.getByte(data + 1);
908
909        if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
910            throw new IOException("Cannot get data: corrupted context");
911        }
912
913        long selatom = 0;
914
915        switch (reason) {
916        case MotifDnDConstants.DRAG_MOTION :
917        case MotifDnDConstants.OPERATION_CHANGED :
918            selatom = sourceAtom;
919            break;
920        case MotifDnDConstants.DROP_START :
921            selatom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
922            break;
923        default:
924            throw new IOException("Cannot get data: invalid message reason");
925        }
926
927        if (selatom == 0) {
928            throw new IOException("Cannot get data: drag source property atom unavailable");
929        }
930
931        long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL;
932                          // with correction of (32-bit unsigned to 64-bit signed) implicit conversion.
933
934        XAtom selectionAtom = XAtom.get(selatom);
935
936        XSelection selection = XSelection.getSelection(selectionAtom);
937        if (selection == null) {
938            selection = new XSelection(selectionAtom);
939        }
940
941        return selection.getData(format, time_stamp);
942    }
943
944    public boolean sendDropDone(long ctxt, boolean success, int dropAction) {
945        XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
946
947        if (xclient.get_message_type() !=
948            MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
949            return false;
950        }
951
952        long data = xclient.get_data();
953        byte reason = (byte)(unsafe.getByte(data) &
954            MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
955        byte origin = (byte)(unsafe.getByte(data) &
956            MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
957        byte eventByteOrder = unsafe.getByte(data + 1);
958
959        if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
960            return false;
961        }
962
963        if (reason != MotifDnDConstants.DROP_START) {
964            return false;
965        }
966
967        long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL;
968                          // with correction of (32-bit unsigned to 64-bit signed) implicit conversion.
969
970        long sel_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
971
972        long status_atom = 0;
973
974        if (success) {
975            status_atom = MotifDnDConstants.XA_XmTRANSFER_SUCCESS.getAtom();
976        } else {
977            status_atom = MotifDnDConstants.XA_XmTRANSFER_FAILURE.getAtom();
978        }
979
980        XToolkit.awtLock();
981        try {
982            XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
983                                          sel_atom,
984                                          status_atom,
985                                          MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom(),
986                                          XWindow.getXAWTRootWindow().getWindow(),
987                                          time_stamp);
988
989            /*
990             * Flush the buffer to guarantee that the drop completion event is sent
991             * to the source before the method returns.
992             */
993            XlibWrapper.XFlush(XToolkit.getDisplay());
994        } finally {
995            XToolkit.awtUnlock();
996        }
997
998        /* Trick to prevent cleanup() from posting dragExit */
999        targetXWindow = null;
1000
1001        /* Cannot do cleanup before the drop finishes as we may need
1002           source protocol version to send drop finished message. */
1003        cleanup();
1004        return true;
1005    }
1006
1007    public final long getSourceWindow() {
1008        return sourceWindow;
1009    }
1010
1011    /**
1012     * Reset the state of the object.
1013     */
1014    public void cleanup() {
1015        // Clear the reference to this protocol.
1016        XDropTargetEventProcessor.reset();
1017
1018        if (targetXWindow != null) {
1019            notifyProtocolListener(targetXWindow, 0, 0,
1020                                   DnDConstants.ACTION_NONE, sourceActions,
1021                                   null, MouseEvent.MOUSE_EXITED);
1022        }
1023
1024        if (sourceWindow != 0) {
1025            XToolkit.awtLock();
1026            try {
1027                XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
1028                XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow,
1029                                         sourceWindowMask);
1030                XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
1031            } finally {
1032                XToolkit.awtUnlock();
1033            }
1034        }
1035
1036        sourceWindow = 0;
1037        sourceWindowMask = 0;
1038        sourceProtocolVersion = 0;
1039        sourceActions = DnDConstants.ACTION_NONE;
1040        sourceFormats = null;
1041        sourceAtom = 0;
1042        userAction = DnDConstants.ACTION_NONE;
1043        sourceX = 0;
1044        sourceY = 0;
1045        targetXWindow = null;
1046        topLevelLeavePostponed = false;
1047    }
1048
1049    public boolean isDragOverComponent() {
1050        return targetXWindow != null;
1051    }
1052
1053    private void notifyProtocolListener(XWindow xwindow, int x, int y,
1054                                        int dropAction, int actions,
1055                                        XClientMessageEvent xclient,
1056                                        int eventID) {
1057        long nativeCtxt = 0;
1058
1059        // Make a copy of the passed XClientMessageEvent structure, since
1060        // the original structure can be freed before this
1061        // SunDropTargetEvent is dispatched.
1062        if (xclient != null) {
1063            int size = XClientMessageEvent.getSize();
1064
1065            nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1066
1067            unsafe.copyMemory(xclient.pData, nativeCtxt, size);
1068        }
1069
1070        getProtocolListener().handleDropTargetNotification(xwindow, x, y,
1071                                                           dropAction,
1072                                                           actions,
1073                                                           sourceFormats,
1074                                                           nativeCtxt,
1075                                                           eventID);
1076    }
1077}
1078