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 
26 package sun.awt.X11;
27 
28 import java.awt.Point;
29 
30 import java.awt.dnd.DnDConstants;
31 
32 import java.awt.event.MouseEvent;
33 
34 import java.io.IOException;
35 
36 import jdk.internal.misc.Unsafe;
37 
38 /**
39  * XDropTargetProtocol implementation for Motif DnD protocol.
40  *
41  * @since 1.5
42  */
43 class 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 
MotifDnDDropTargetProtocol(XDropTargetProtocolListener listener)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}.
66      */
createInstance(XDropTargetProtocolListener listener)67     static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
68         return new MotifDnDDropTargetProtocol(listener);
69     }
70 
getProtocolName()71     public String getProtocolName() {
72         return XDragAndDropProtocols.MotifDnD;
73     }
74 
registerDropTarget(long window)75     public void registerDropTarget(long window) {
76         assert XToolkit.isAWTLockHeldByCurrentThread();
77 
78         MotifDnDConstants.writeDragReceiverInfoStruct(window);
79     }
80 
unregisterDropTarget(long window)81     public void unregisterDropTarget(long window) {
82         assert XToolkit.isAWTLockHeldByCurrentThread();
83 
84         MotifDnDConstants.XA_MOTIF_ATOM_0.DeleteProperty(window);
85     }
86 
registerEmbedderDropSite(long embedder)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 
unregisterEmbedderDropSite(long embedder)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      */
registerEmbeddedDropSite(long embedded)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 
isProtocolSupported(long window)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 
processTopLevelEnter(XClientMessageEvent xclient)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 
processDragMotion(XClientMessageEvent xclient)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 
processTopLevelLeave(XClientMessageEvent xclient)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 
processDropStart(XClientMessageEvent xclient)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 
getMessageType(XClientMessageEvent xclient)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 
processClientMessageImpl(XClientMessageEvent xclient)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      */
sendEnterMessageToToplevel(long win, XClientMessageEvent xclient)754     protected void sendEnterMessageToToplevel(long win,
755                                               XClientMessageEvent xclient) {
756         throw new Error("UNIMPLEMENTED");
757     }
758 
sendLeaveMessageToToplevel(long win, XClientMessageEvent xclient)759     protected void sendLeaveMessageToToplevel(long win,
760                                               XClientMessageEvent xclient) {
761         throw new Error("UNIMPLEMENTED");
762     }
763 
forwardEventToEmbedded(long embedded, long ctxt, int eventID)764     public boolean forwardEventToEmbedded(long embedded, long ctxt,
765                                           int eventID) {
766         // UNIMPLEMENTED.
767         return false;
768     }
769 
isXEmbedSupported()770     public boolean isXEmbedSupported() {
771         return false;
772     }
773 
sendResponse(long ctxt, int eventID, int action)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 
getData(long ctxt, long format)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 
sendDropDone(long ctxt, boolean success, int dropAction)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 
getSourceWindow()1007     public final long getSourceWindow() {
1008         return sourceWindow;
1009     }
1010 
1011     /**
1012      * Reset the state of the object.
1013      */
cleanup()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 
isDragOverComponent()1049     public boolean isDragOverComponent() {
1050         return targetXWindow != null;
1051     }
1052 
notifyProtocolListener(XWindow xwindow, int x, int y, int dropAction, int actions, XClientMessageEvent xclient, int eventID)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