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 sun.util.logging.PlatformLogger;
37 
38 import jdk.internal.misc.Unsafe;
39 
40 /**
41  * XDropTargetProtocol implementation for XDnD protocol.
42  *
43  * @since 1.5
44  */
45 class XDnDDropTargetProtocol extends XDropTargetProtocol {
46     private static final PlatformLogger logger =
47         PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDropTargetProtocol");
48 
49     private static final Unsafe unsafe = XlibWrapper.unsafe;
50 
51     private long sourceWindow = 0;
52     private long sourceWindowMask = 0;
53     private int sourceProtocolVersion = 0;
54     private int sourceActions = DnDConstants.ACTION_NONE;
55     private long[] sourceFormats = null;
56     private boolean trackSourceActions = false;
57     private int userAction = DnDConstants.ACTION_NONE;
58     private int sourceX = 0;
59     private int sourceY = 0;
60     private XWindow targetXWindow = null;
61 
62     // XEmbed stuff.
63     private long prevCtxt = 0;
64     private boolean overXEmbedClient = false;
65 
XDnDDropTargetProtocol(XDropTargetProtocolListener listener)66     protected XDnDDropTargetProtocol(XDropTargetProtocolListener listener) {
67         super(listener);
68     }
69 
70     /**
71      * Creates an instance associated with the specified listener.
72      *
73      * @throws NullPointerException if listener is {@code null}.
74      */
createInstance(XDropTargetProtocolListener listener)75     static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
76         return new XDnDDropTargetProtocol(listener);
77     }
78 
getProtocolName()79     public String getProtocolName() {
80         return XDragAndDropProtocols.XDnD;
81     }
82 
registerDropTarget(long window)83     public void registerDropTarget(long window) {
84         assert XToolkit.isAWTLockHeldByCurrentThread();
85 
86         long data = Native.allocateLongArray(1);
87 
88         try {
89             Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
90 
91             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
92             XDnDConstants.XA_XdndAware.setAtomData(window, XAtom.XA_ATOM, data, 1);
93             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
94 
95             if ((XErrorHandlerUtil.saved_error != null) &&
96                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
97                 throw new XException("Cannot write XdndAware property");
98             }
99         } finally {
100             unsafe.freeMemory(data);
101             data = 0;
102         }
103     }
104 
unregisterDropTarget(long window)105     public void unregisterDropTarget(long window) {
106         assert XToolkit.isAWTLockHeldByCurrentThread();
107 
108         XDnDConstants.XA_XdndAware.DeleteProperty(window);
109     }
110 
registerEmbedderDropSite(long embedder)111     public void registerEmbedderDropSite(long embedder) {
112         assert XToolkit.isAWTLockHeldByCurrentThread();
113 
114         boolean overriden = false;
115         int version = 0;
116         long proxy = 0;
117         long newProxy = XDropTargetRegistry.getDnDProxyWindow();
118         int status = 0;
119 
120         WindowPropertyGetter wpg1 =
121             new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndAware, 0, 1,
122                                      false, XConstants.AnyPropertyType);
123 
124         try {
125             status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
126 
127             if (status == XConstants.Success &&
128                 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
129 
130                 overriden = true;
131                 version = (int)Native.getLong(wpg1.getData());
132             }
133         } finally {
134             wpg1.dispose();
135         }
136 
137         /* XdndProxy is not supported for prior to XDnD version 4 */
138         if (overriden && version >= 4) {
139             WindowPropertyGetter wpg2 =
140                 new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndProxy,
141                                          0, 1, false, XAtom.XA_WINDOW);
142 
143             try {
144                 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
145 
146                 if (status == XConstants.Success &&
147                     wpg2.getData() != 0 &&
148                     wpg2.getActualType() == XAtom.XA_WINDOW) {
149 
150                     proxy = Native.getLong(wpg2.getData());
151                 }
152             } finally {
153                 wpg2.dispose();
154             }
155 
156             if (proxy != 0) {
157                 WindowPropertyGetter wpg3 =
158                     new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
159                                              0, 1, false, XAtom.XA_WINDOW);
160 
161                 try {
162                     status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
163 
164                     if (status != XConstants.Success ||
165                         wpg3.getData() == 0 ||
166                         wpg3.getActualType() != XAtom.XA_WINDOW ||
167                         Native.getLong(wpg3.getData()) != proxy) {
168 
169                         proxy = 0;
170                     } else {
171                         WindowPropertyGetter wpg4 =
172                             new WindowPropertyGetter(proxy,
173                                                      XDnDConstants.XA_XdndAware,
174                                                      0, 1, false,
175                                                      XConstants.AnyPropertyType);
176 
177                         try {
178                             status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
179 
180                             if (status != XConstants.Success ||
181                                 wpg4.getData() == 0 ||
182                                 wpg4.getActualType() != XAtom.XA_ATOM) {
183 
184                                 proxy = 0;
185                             }
186                         } finally {
187                             wpg4.dispose();
188                         }
189                     }
190                 } finally {
191                     wpg3.dispose();
192                 }
193             }
194         }
195 
196         if (proxy == newProxy) {
197             // Embedder already registered.
198             return;
199         }
200 
201         long data = Native.allocateLongArray(1);
202 
203         try {
204             Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
205 
206             /* The proxy window must have the XdndAware set, as XDnD protocol
207                prescribes to check the proxy window for XdndAware. */
208             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
209             XDnDConstants.XA_XdndAware.setAtomData(newProxy, XAtom.XA_ATOM,
210                                                    data, 1);
211             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
212 
213             if ((XErrorHandlerUtil.saved_error != null) &&
214                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
215                 throw new XException("Cannot write XdndAware property");
216             }
217 
218             Native.putLong(data, 0, newProxy);
219 
220             /* The proxy window must have the XdndProxy set to point to itself.*/
221             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
222             XDnDConstants.XA_XdndProxy.setAtomData(newProxy, XAtom.XA_WINDOW,
223                                                    data, 1);
224             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
225 
226             if ((XErrorHandlerUtil.saved_error != null) &&
227                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
228                 throw new XException("Cannot write XdndProxy property");
229             }
230 
231             Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
232 
233             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
234             XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM,
235                                                    data, 1);
236             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
237 
238             if ((XErrorHandlerUtil.saved_error != null) &&
239                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
240                 throw new XException("Cannot write XdndAware property");
241             }
242 
243             Native.putLong(data, 0, newProxy);
244 
245             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
246             XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW,
247                                                    data, 1);
248             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
249 
250             if ((XErrorHandlerUtil.saved_error != null) &&
251                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
252                 throw new XException("Cannot write XdndProxy property");
253             }
254         } finally {
255             unsafe.freeMemory(data);
256             data = 0;
257         }
258 
259         putEmbedderRegistryEntry(embedder, overriden, version, proxy);
260     }
261 
unregisterEmbedderDropSite(long embedder)262     public void unregisterEmbedderDropSite(long embedder) {
263         assert XToolkit.isAWTLockHeldByCurrentThread();
264 
265         EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder);
266 
267         if (entry == null) {
268             return;
269         }
270 
271         if (entry.isOverriden()) {
272             long data = Native.allocateLongArray(1);
273 
274             try {
275                 Native.putLong(data, 0, entry.getVersion());
276 
277                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
278                 XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM,
279                                                        data, 1);
280                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
281 
282                 if ((XErrorHandlerUtil.saved_error != null) &&
283                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
284                     throw new XException("Cannot write XdndAware property");
285                 }
286 
287                 Native.putLong(data, 0, (int)entry.getProxy());
288 
289                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
290                 XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW,
291                                                        data, 1);
292                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
293 
294                 if ((XErrorHandlerUtil.saved_error != null) &&
295                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
296                     throw new XException("Cannot write XdndProxy property");
297                 }
298             } finally {
299                 unsafe.freeMemory(data);
300                 data = 0;
301             }
302         } else {
303             XDnDConstants.XA_XdndAware.DeleteProperty(embedder);
304             XDnDConstants.XA_XdndProxy.DeleteProperty(embedder);
305         }
306     }
307 
308     /*
309      * Gets and stores in the registry the embedder's XDnD drop site info
310      * from the embedded.
311      */
registerEmbeddedDropSite(long embedded)312     public void registerEmbeddedDropSite(long embedded) {
313         assert XToolkit.isAWTLockHeldByCurrentThread();
314 
315         boolean overriden = false;
316         int version = 0;
317         long proxy = 0;
318         long newProxy = XDropTargetRegistry.getDnDProxyWindow();
319         int status = 0;
320 
321         WindowPropertyGetter wpg1 =
322             new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndAware, 0, 1,
323                                      false, XConstants.AnyPropertyType);
324 
325         try {
326             status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
327 
328             if (status == XConstants.Success &&
329                 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
330 
331                 overriden = true;
332                 version = (int)Native.getLong(wpg1.getData());
333             }
334         } finally {
335             wpg1.dispose();
336         }
337 
338         /* XdndProxy is not supported for prior to XDnD version 4 */
339         if (overriden && version >= 4) {
340             WindowPropertyGetter wpg2 =
341                 new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndProxy,
342                                          0, 1, false, XAtom.XA_WINDOW);
343 
344             try {
345                 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
346 
347                 if (status == XConstants.Success &&
348                     wpg2.getData() != 0 &&
349                     wpg2.getActualType() == XAtom.XA_WINDOW) {
350 
351                     proxy = Native.getLong(wpg2.getData());
352                 }
353             } finally {
354                 wpg2.dispose();
355             }
356 
357             if (proxy != 0) {
358                 WindowPropertyGetter wpg3 =
359                     new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
360                                              0, 1, false, XAtom.XA_WINDOW);
361 
362                 try {
363                     status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
364 
365                     if (status != XConstants.Success ||
366                         wpg3.getData() == 0 ||
367                         wpg3.getActualType() != XAtom.XA_WINDOW ||
368                         Native.getLong(wpg3.getData()) != proxy) {
369 
370                         proxy = 0;
371                     } else {
372                         WindowPropertyGetter wpg4 =
373                             new WindowPropertyGetter(proxy,
374                                                      XDnDConstants.XA_XdndAware,
375                                                      0, 1, false,
376                                                      XConstants.AnyPropertyType);
377 
378                         try {
379                             status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
380 
381                             if (status != XConstants.Success ||
382                                 wpg4.getData() == 0 ||
383                                 wpg4.getActualType() != XAtom.XA_ATOM) {
384 
385                                 proxy = 0;
386                             }
387                         } finally {
388                             wpg4.dispose();
389                         }
390                     }
391                 } finally {
392                     wpg3.dispose();
393                 }
394             }
395         }
396 
397         putEmbedderRegistryEntry(embedded, overriden, version, proxy);
398     }
399 
isProtocolSupported(long window)400     public boolean isProtocolSupported(long window) {
401         assert XToolkit.isAWTLockHeldByCurrentThread();
402 
403         WindowPropertyGetter wpg1 =
404             new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1,
405                                      false, XConstants.AnyPropertyType);
406 
407         try {
408             int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
409 
410             if (status == XConstants.Success &&
411                 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
412 
413                 return true;
414             } else {
415                 return false;
416             }
417         } finally {
418             wpg1.dispose();
419         }
420     }
421 
processXdndEnter(XClientMessageEvent xclient)422     private boolean processXdndEnter(XClientMessageEvent xclient) {
423         long source_win = 0;
424         long source_win_mask = 0;
425         int protocol_version = 0;
426         int actions = DnDConstants.ACTION_NONE;
427         boolean track = true;
428         long[] formats = null;
429 
430         if (getSourceWindow() != 0) {
431             return false;
432         }
433 
434         if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow)
435             && getEmbedderRegistryEntry(xclient.get_window()) == null) {
436             return false;
437         }
438 
439         if (xclient.get_message_type() != XDnDConstants.XA_XdndEnter.getAtom()){
440             return false;
441         }
442 
443         protocol_version =
444             (int)((xclient.get_data(1) & XDnDConstants.XDND_PROTOCOL_MASK) >>
445                   XDnDConstants.XDND_PROTOCOL_SHIFT);
446 
447         /* XDnD compliance only requires supporting version 3 and up. */
448         if (protocol_version < XDnDConstants.XDND_MIN_PROTOCOL_VERSION) {
449             return false;
450         }
451 
452         /* Ignore the source if the protocol version is higher than we support. */
453         if (protocol_version > XDnDConstants.XDND_PROTOCOL_VERSION) {
454             return false;
455         }
456 
457         source_win = xclient.get_data(0);
458 
459         /* Extract the list of supported actions. */
460         if (protocol_version < 2) {
461             /* Prior to XDnD version 2 only COPY action was supported. */
462             actions = DnDConstants.ACTION_COPY;
463         } else {
464             WindowPropertyGetter wpg =
465                 new WindowPropertyGetter(source_win,
466                                          XDnDConstants.XA_XdndActionList,
467                                          0, 0xFFFF, false,
468                                          XAtom.XA_ATOM);
469             try {
470                 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
471 
472                 if (wpg.getActualType() == XAtom.XA_ATOM &&
473                     wpg.getActualFormat() == 32) {
474                     long data = wpg.getData();
475 
476                     for (int i = 0; i < wpg.getNumberOfItems(); i++) {
477                         actions |=
478                             XDnDConstants.getJavaActionForXDnDAction(Native.getLong(data, i));
479                     }
480                 } else {
481                     /*
482                      * According to XDnD protocol, XdndActionList is optional.
483                      * If XdndActionList is not set we try to guess which actions are
484                      * supported.
485                      */
486                     actions = DnDConstants.ACTION_COPY;
487                     track = true;
488                 }
489             } finally {
490                 wpg.dispose();
491             }
492         }
493 
494         /* Extract the available data types. */
495         if ((xclient.get_data(1) & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) {
496             WindowPropertyGetter wpg =
497                 new WindowPropertyGetter(source_win,
498                                          XDnDConstants.XA_XdndTypeList,
499                                          0, 0xFFFF, false,
500                                          XAtom.XA_ATOM);
501             try {
502                 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
503 
504                 if (wpg.getActualType() == XAtom.XA_ATOM &&
505                     wpg.getActualFormat() == 32) {
506                     formats = Native.toLongs(wpg.getData(),
507                                              wpg.getNumberOfItems());
508                 } else {
509                     formats = new long[0];
510                 }
511             } finally {
512                 wpg.dispose();
513             }
514         } else {
515             int countFormats = 0;
516             long[] formats3 = new long[3];
517 
518             for (int i = 0; i < 3; i++) {
519                 long j;
520                 if ((j = xclient.get_data(2 + i)) != XConstants.None) {
521                     formats3[countFormats++] = j;
522                 }
523             }
524 
525             formats = new long[countFormats];
526 
527             System.arraycopy(formats3, 0, formats, 0, countFormats);
528         }
529 
530         assert XToolkit.isAWTLockHeldByCurrentThread();
531 
532         /*
533          * Select for StructureNotifyMask to receive DestroyNotify in case of source
534          * crash.
535          */
536         XWindowAttributes wattr = new XWindowAttributes();
537         try {
538             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
539             int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
540                                                           source_win, wattr.pData);
541 
542             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
543 
544             if ((status == 0) ||
545                 ((XErrorHandlerUtil.saved_error != null) &&
546                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
547                 throw new XException("XGetWindowAttributes failed");
548             }
549 
550             source_win_mask = wattr.get_your_event_mask();
551         } finally {
552             wattr.dispose();
553         }
554 
555         XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
556         XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win,
557                                  source_win_mask |
558                                  XConstants.StructureNotifyMask);
559 
560         XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
561 
562         if ((XErrorHandlerUtil.saved_error != null) &&
563             (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
564             throw new XException("XSelectInput failed");
565         }
566 
567         sourceWindow = source_win;
568         sourceWindowMask = source_win_mask;
569         sourceProtocolVersion = protocol_version;
570         sourceActions = actions;
571         sourceFormats = formats;
572         trackSourceActions = track;
573 
574         return true;
575     }
576 
processXdndPosition(XClientMessageEvent xclient)577     private boolean processXdndPosition(XClientMessageEvent xclient) {
578         long time_stamp = (int)XConstants.CurrentTime;
579         long xdnd_action = 0;
580         int java_action = DnDConstants.ACTION_NONE;
581         int x = 0;
582         int y = 0;
583 
584         /* Ignore XDnD messages from all other windows. */
585         if (sourceWindow != xclient.get_data(0)) {
586             return false;
587         }
588 
589         XWindow xwindow = null;
590         {
591             XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
592             if (xbasewindow instanceof XWindow) {
593                 xwindow = (XWindow)xbasewindow;
594             }
595         }
596 
597         x = (int)(xclient.get_data(2) >> 16);
598         y = (int)(xclient.get_data(2) & 0xFFFF);
599 
600         if (xwindow == null) {
601             long receiver =
602                 XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
603                     xclient.get_window(), x, y);
604 
605             if (receiver != 0) {
606                 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
607                 if (xbasewindow instanceof XWindow) {
608                     xwindow = (XWindow)xbasewindow;
609                 }
610             }
611         }
612 
613         if (xwindow != null) {
614             /* Translate mouse position from root coordinates
615                to the target window coordinates. */
616             Point p = xwindow.toLocal(x, y);
617             x = p.x;
618             y = p.y;
619         }
620 
621         /* Time stamp - new in XDnD version 1. */
622         if (sourceProtocolVersion > 0) {
623             time_stamp = xclient.get_data(3) & 0xFFFFFFFFL;
624         }
625 
626         /* User action - new in XDnD version 2. */
627         if (sourceProtocolVersion > 1) {
628             xdnd_action = xclient.get_data(4);
629         } else {
630             /* The default action is XdndActionCopy */
631             xdnd_action = XDnDConstants.XA_XdndActionCopy.getAtom();
632         }
633 
634         java_action = XDnDConstants.getJavaActionForXDnDAction(xdnd_action);
635 
636         if (trackSourceActions) {
637             sourceActions |= java_action;
638         }
639 
640         if (xwindow == null) {
641             if (targetXWindow != null) {
642                 notifyProtocolListener(targetXWindow, x, y,
643                                        DnDConstants.ACTION_NONE, xclient,
644                                        MouseEvent.MOUSE_EXITED);
645             }
646         } else {
647             int java_event_id = 0;
648 
649             if (targetXWindow == null) {
650                 java_event_id = MouseEvent.MOUSE_ENTERED;
651             } else {
652                 java_event_id = MouseEvent.MOUSE_DRAGGED;
653             }
654 
655             notifyProtocolListener(xwindow, x, y, java_action, xclient,
656                                    java_event_id);
657         }
658 
659         userAction = java_action;
660         sourceX = x;
661         sourceY = y;
662         targetXWindow = xwindow;
663 
664         return true;
665     }
666 
processXdndLeave(XClientMessageEvent xclient)667     private boolean processXdndLeave(XClientMessageEvent xclient) {
668         /* Ignore XDnD messages from all other windows. */
669         if (sourceWindow != xclient.get_data(0)) {
670             return false;
671         }
672 
673         cleanup();
674 
675         return true;
676     }
677 
processXdndDrop(XClientMessageEvent xclient)678     private boolean processXdndDrop(XClientMessageEvent xclient) {
679         /* Ignore XDnD messages from all other windows. */
680         if (sourceWindow != xclient.get_data(0)) {
681             return false;
682         }
683 
684         if (targetXWindow != null) {
685             notifyProtocolListener(targetXWindow, sourceX, sourceY, userAction,
686                                    xclient, MouseEvent.MOUSE_RELEASED);
687         }
688 
689         return true;
690     }
691 
getMessageType(XClientMessageEvent xclient)692     public int getMessageType(XClientMessageEvent xclient) {
693         long messageType = xclient.get_message_type();
694 
695         if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) {
696             return ENTER_MESSAGE;
697         } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) {
698             return MOTION_MESSAGE;
699         } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) {
700             return LEAVE_MESSAGE;
701         } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) {
702             return DROP_MESSAGE;
703         } else {
704             return UNKNOWN_MESSAGE;
705         }
706     }
707 
processClientMessageImpl(XClientMessageEvent xclient)708     protected boolean processClientMessageImpl(XClientMessageEvent xclient) {
709         long messageType = xclient.get_message_type();
710 
711         if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) {
712             return processXdndEnter(xclient);
713         } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) {
714             return processXdndPosition(xclient);
715         } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) {
716             return processXdndLeave(xclient);
717         } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) {
718             return processXdndDrop(xclient);
719         } else {
720             return false;
721         }
722     }
723 
sendEnterMessageToToplevel(long toplevel, XClientMessageEvent xclient)724     protected void sendEnterMessageToToplevel(long toplevel,
725                                               XClientMessageEvent xclient) {
726         /* flags */
727         long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT;
728         if (sourceFormats != null && sourceFormats.length > 3) {
729             data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
730         }
731         long data2 = sourceFormats.length > 0 ? sourceFormats[0] : 0;
732         long data3 = sourceFormats.length > 1 ? sourceFormats[1] : 0;
733         long data4 = sourceFormats.length > 2 ? sourceFormats[2] : 0;
734         sendEnterMessageToToplevelImpl(toplevel, xclient.get_data(0),
735                                        data1, data2, data3, data4);
736 
737     }
738 
sendEnterMessageToToplevelImpl(long toplevel, long sourceWindow, long data1, long data2, long data3, long data4)739     private void sendEnterMessageToToplevelImpl(long toplevel,
740                                                 long sourceWindow,
741                                                 long data1, long data2,
742                                                 long data3, long data4) {
743         XClientMessageEvent enter = new XClientMessageEvent();
744         try {
745             enter.set_type(XConstants.ClientMessage);
746             enter.set_window(toplevel);
747             enter.set_format(32);
748             enter.set_message_type(XDnDConstants.XA_XdndEnter.getAtom());
749             /* XID of the source window */
750             enter.set_data(0, sourceWindow);
751             enter.set_data(1, data1);
752             enter.set_data(2, data2);
753             enter.set_data(3, data3);
754             enter.set_data(4, data4);
755 
756             forwardClientMessageToToplevel(toplevel, enter);
757         } finally {
758             enter.dispose();
759         }
760     }
761 
sendLeaveMessageToToplevel(long toplevel, XClientMessageEvent xclient)762     protected void sendLeaveMessageToToplevel(long toplevel,
763                                               XClientMessageEvent xclient) {
764         sendLeaveMessageToToplevelImpl(toplevel, xclient.get_data(0));
765     }
766 
sendLeaveMessageToToplevelImpl(long toplevel, long sourceWindow)767     protected void sendLeaveMessageToToplevelImpl(long toplevel,
768                                                   long sourceWindow) {
769         XClientMessageEvent leave = new XClientMessageEvent();
770         try {
771             leave.set_type(XConstants.ClientMessage);
772             leave.set_window(toplevel);
773             leave.set_format(32);
774             leave.set_message_type(XDnDConstants.XA_XdndLeave.getAtom());
775             /* XID of the source window */
776             leave.set_data(0, sourceWindow);
777             /* flags */
778             leave.set_data(1, 0);
779 
780             forwardClientMessageToToplevel(toplevel, leave);
781         } finally {
782             leave.dispose();
783         }
784     }
785 
sendResponse(long ctxt, int eventID, int action)786     public boolean sendResponse(long ctxt, int eventID, int action) {
787         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
788 
789         if (xclient.get_message_type() !=
790             XDnDConstants.XA_XdndPosition.getAtom()) {
791 
792             return false;
793         }
794 
795         if (eventID == MouseEvent.MOUSE_EXITED) {
796             action = DnDConstants.ACTION_NONE;
797         }
798 
799         XClientMessageEvent msg = new XClientMessageEvent();
800         try {
801             msg.set_type(XConstants.ClientMessage);
802             msg.set_window(xclient.get_data(0));
803             msg.set_format(32);
804             msg.set_message_type(XDnDConstants.XA_XdndStatus.getAtom());
805             /* target window */
806             msg.set_data(0, xclient.get_window());
807             /* flags */
808             long flags = 0;
809             if (action != DnDConstants.ACTION_NONE) {
810                 flags |= XDnDConstants.XDND_ACCEPT_DROP_FLAG;
811             }
812             msg.set_data(1, flags);
813             /* specify an empty rectangle */
814             msg.set_data(2, 0); /* x, y */
815             msg.set_data(3, 0); /* w, h */
816             /* action accepted by the target */
817             msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(action));
818 
819             XToolkit.awtLock();
820             try {
821                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
822                                        xclient.get_data(0),
823                                        false, XConstants.NoEventMask,
824                                        msg.pData);
825             } finally {
826                 XToolkit.awtUnlock();
827             }
828         } finally {
829             msg.dispose();
830         }
831 
832         return true;
833     }
834 
getData(long ctxt, long format)835     public Object getData(long ctxt, long format)
836       throws IllegalArgumentException, IOException {
837         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
838         long message_type = xclient.get_message_type();
839         long time_stamp = XConstants.CurrentTime;
840 
841         // NOTE: we assume that the source supports at least version 1, so we
842         // can use the time stamp
843         if (message_type == XDnDConstants.XA_XdndPosition.getAtom()) {
844             // X server time is an unsigned 32-bit number!
845             time_stamp = xclient.get_data(3) & 0xFFFFFFFFL;
846         } else if (message_type == XDnDConstants.XA_XdndDrop.getAtom()) {
847             // X server time is an unsigned 32-bit number!
848             time_stamp = xclient.get_data(2) & 0xFFFFFFFFL;
849         } else {
850             throw new IllegalArgumentException();
851         }
852 
853         return XDnDConstants.XDnDSelection.getData(format, time_stamp);
854     }
855 
sendDropDone(long ctxt, boolean success, int dropAction)856     public boolean sendDropDone(long ctxt, boolean success, int dropAction) {
857         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
858 
859         if (xclient.get_message_type() !=
860             XDnDConstants.XA_XdndDrop.getAtom()) {
861             return false;
862         }
863 
864         /*
865          * The XDnD protocol recommends that the target requests the special
866          * target DELETE in case if the drop action is XdndActionMove.
867          */
868         if (dropAction == DnDConstants.ACTION_MOVE && success) {
869 
870             long time_stamp = xclient.get_data(2) & 0xFFFFFFFFL;
871             long xdndSelectionAtom =
872                 XDnDConstants.XDnDSelection.getSelectionAtom().getAtom();
873 
874             XToolkit.awtLock();
875             try {
876                 XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
877                                               xdndSelectionAtom,
878                                               XAtom.get("DELETE").getAtom(),
879                                               XAtom.get("XAWT_SELECTION").getAtom(),
880                                               XWindow.getXAWTRootWindow().getWindow(),
881                                               time_stamp);
882             } finally {
883                 XToolkit.awtUnlock();
884             }
885         }
886 
887         XClientMessageEvent msg = new XClientMessageEvent();
888         try {
889             msg.set_type(XConstants.ClientMessage);
890             msg.set_window(xclient.get_data(0));
891             msg.set_format(32);
892             msg.set_message_type(XDnDConstants.XA_XdndFinished.getAtom());
893             msg.set_data(0, xclient.get_window()); /* target window */
894             msg.set_data(1, 0); /* flags */
895             /* specify an empty rectangle */
896             msg.set_data(2, 0);
897             if (sourceProtocolVersion >= 5) {
898                 if (success) {
899                     msg.set_data(1, XDnDConstants.XDND_ACCEPT_DROP_FLAG);
900                 }
901                 /* action performed by the target */
902                 msg.set_data(2, XDnDConstants.getXDnDActionForJavaAction(dropAction));
903             }
904             msg.set_data(3, 0);
905             msg.set_data(4, 0);
906 
907             XToolkit.awtLock();
908             try {
909                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
910                                        xclient.get_data(0),
911                                        false, XConstants.NoEventMask,
912                                        msg.pData);
913             } finally {
914                 XToolkit.awtUnlock();
915             }
916         } finally {
917             msg.dispose();
918         }
919 
920         /*
921          * Flush the buffer to guarantee that the drop completion event is sent
922          * to the source before the method returns.
923          */
924         XToolkit.awtLock();
925         try {
926             XlibWrapper.XFlush(XToolkit.getDisplay());
927         } finally {
928             XToolkit.awtUnlock();
929         }
930 
931         /* Trick to prevent cleanup() from posting dragExit */
932         targetXWindow = null;
933 
934         /* Cannot do cleanup before the drop finishes as we may need
935            source protocol version to send drop finished message. */
936         cleanup();
937         return true;
938     }
939 
getSourceWindow()940     public final long getSourceWindow() {
941         return sourceWindow;
942     }
943 
944     /**
945      * Reset the state of the object.
946      */
cleanup()947     public void cleanup() {
948         // Clear the reference to this protocol.
949         XDropTargetEventProcessor.reset();
950 
951         if (targetXWindow != null) {
952             notifyProtocolListener(targetXWindow, 0, 0,
953                                    DnDConstants.ACTION_NONE, null,
954                                    MouseEvent.MOUSE_EXITED);
955         }
956 
957         if (sourceWindow != 0) {
958             XToolkit.awtLock();
959             try {
960                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
961                 XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow,
962                                          sourceWindowMask);
963                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
964             } finally {
965                 XToolkit.awtUnlock();
966             }
967         }
968 
969         sourceWindow = 0;
970         sourceWindowMask = 0;
971         sourceProtocolVersion = 0;
972         sourceActions = DnDConstants.ACTION_NONE;
973         sourceFormats = null;
974         trackSourceActions = false;
975         userAction = DnDConstants.ACTION_NONE;
976         sourceX = 0;
977         sourceY = 0;
978         targetXWindow = null;
979     }
980 
isDragOverComponent()981     public boolean isDragOverComponent() {
982         return targetXWindow != null;
983     }
984 
adjustEventForForwarding(XClientMessageEvent xclient, EmbedderRegistryEntry entry)985     public void adjustEventForForwarding(XClientMessageEvent xclient,
986                                          EmbedderRegistryEntry entry) {
987         /* Adjust the event to match the XDnD protocol version. */
988         int version = entry.getVersion();
989         if (xclient.get_message_type() == XDnDConstants.XA_XdndEnter.getAtom()) {
990             int min_version = sourceProtocolVersion < version ?
991                 sourceProtocolVersion : version;
992             long data1 = min_version << XDnDConstants.XDND_PROTOCOL_SHIFT;
993             if (sourceFormats != null && sourceFormats.length > 3) {
994                 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
995             }
996             if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
997                 logger.finest("         "
998                               + " entryVersion=" + version
999                               + " sourceProtocolVersion=" +
1000                               sourceProtocolVersion
1001                               + " sourceFormats.length=" +
1002                               (sourceFormats != null ? sourceFormats.length : 0));
1003             }
1004             xclient.set_data(1, data1);
1005         }
1006     }
1007 
1008     @SuppressWarnings("static")
1009     private void notifyProtocolListener(XWindow xwindow, int x, int y,
1010                                         int dropAction,
1011                                         XClientMessageEvent xclient,
1012                                         int eventID) {
1013         long nativeCtxt = 0;
1014 
1015         // Make a copy of the passed XClientMessageEvent structure, since
1016         // the original structure can be freed before this
1017         // SunDropTargetEvent is dispatched.
1018         if (xclient != null) {
1019             int size = new XClientMessageEvent(nativeCtxt).getSize();
1020 
1021             nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1022 
1023             unsafe.copyMemory(xclient.pData, nativeCtxt, size);
1024 
1025             long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT;
1026             if (sourceFormats != null && sourceFormats.length > 3) {
1027                 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
1028             }
1029             // Append information from the latest XdndEnter event.
1030             Native.putLong(nativeCtxt + size, data1);
1031             Native.putLong(nativeCtxt + size + Native.getLongSize(),
1032                            sourceFormats.length > 0 ? sourceFormats[0] : 0);
1033             Native.putLong(nativeCtxt + size + 2 * Native.getLongSize(),
1034                            sourceFormats.length > 1 ? sourceFormats[1] : 0);
1035             Native.putLong(nativeCtxt + size + 3 * Native.getLongSize(),
1036                            sourceFormats.length > 2 ? sourceFormats[2] : 0);
1037         }
1038 
1039         getProtocolListener().handleDropTargetNotification(xwindow, x, y,
1040                                                            dropAction,
1041                                                            sourceActions,
1042                                                            sourceFormats,
1043                                                            nativeCtxt,
1044                                                            eventID);
1045     }
1046 
1047     /*
1048      * The methods/fields defined below are executed/accessed only on
1049      * the toolkit thread.
1050      * The methods/fields defined below are executed/accessed only on the event
1051      * dispatch thread.
1052      */
1053 
forwardEventToEmbedded(long embedded, long ctxt, int eventID)1054     public boolean forwardEventToEmbedded(long embedded, long ctxt,
1055                                           int eventID) {
1056         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1057             logger.finest("        ctxt=" + ctxt +
1058                           " type=" + (ctxt != 0 ?
1059                                       getMessageType(new
1060                                           XClientMessageEvent(ctxt)) : 0) +
1061                           " prevCtxt=" + prevCtxt +
1062                           " prevType=" + (prevCtxt != 0 ?
1063                                       getMessageType(new
1064                                           XClientMessageEvent(prevCtxt)) : 0));
1065         }
1066         if ((ctxt == 0 ||
1067              getMessageType(new XClientMessageEvent(ctxt)) == UNKNOWN_MESSAGE) &&
1068             (prevCtxt == 0 ||
1069              getMessageType(new XClientMessageEvent(prevCtxt)) == UNKNOWN_MESSAGE)) {
1070             return false;
1071         }
1072 
1073         // The size of XClientMessageEvent structure.
1074         int size = XClientMessageEvent.getSize();
1075 
1076         if (ctxt != 0) {
1077             XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
1078             if (!overXEmbedClient) {
1079                 long data1 = Native.getLong(ctxt + size);
1080                 long data2 = Native.getLong(ctxt + size + Native.getLongSize());
1081                 long data3 = Native.getLong(ctxt + size + 2 * Native.getLongSize());
1082                 long data4 = Native.getLong(ctxt + size + 3 * Native.getLongSize());
1083 
1084                 if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1085                     logger.finest("         1 "
1086                                   + " embedded=" + embedded
1087                                   + " source=" + xclient.get_data(0)
1088                                   + " data1=" + data1
1089                                   + " data2=" + data2
1090                                   + " data3=" + data3
1091                                   + " data4=" + data4);
1092                 }
1093 
1094                 // Copy XdndTypeList from source to proxy.
1095                 if ((data1 & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) {
1096                     WindowPropertyGetter wpg =
1097                         new WindowPropertyGetter(xclient.get_data(0),
1098                                                  XDnDConstants.XA_XdndTypeList,
1099                                                  0, 0xFFFF, false,
1100                                                  XAtom.XA_ATOM);
1101                     try {
1102                         wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
1103 
1104                         if (wpg.getActualType() == XAtom.XA_ATOM &&
1105                             wpg.getActualFormat() == 32) {
1106 
1107                             XToolkit.awtLock();
1108                             try {
1109                                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
1110                                 XDnDConstants.XA_XdndTypeList.setAtomData(xclient.get_window(),
1111                                                                           XAtom.XA_ATOM,
1112                                                                           wpg.getData(),
1113                                                                           wpg.getNumberOfItems());
1114                                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
1115 
1116                                 if ((XErrorHandlerUtil.saved_error != null) &&
1117                                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
1118                                     if (logger.isLoggable(PlatformLogger.Level.WARNING)) {
1119                                         logger.warning("Cannot set XdndTypeList on the proxy window");
1120                                     }
1121                                 }
1122                             } finally {
1123                                 XToolkit.awtUnlock();
1124                             }
1125                         } else {
1126                             if (logger.isLoggable(PlatformLogger.Level.WARNING)) {
1127                                 logger.warning("Cannot read XdndTypeList from the source window");
1128                             }
1129                         }
1130                     } finally {
1131                         wpg.dispose();
1132                     }
1133                 }
1134                 XDragSourceContextPeer.setProxyModeSourceWindow(xclient.get_data(0));
1135 
1136                 sendEnterMessageToToplevelImpl(embedded, xclient.get_window(),
1137                                                data1, data2, data3, data4);
1138                 overXEmbedClient = true;
1139             }
1140 
1141             if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1142                 logger.finest("         2 "
1143                               + " embedded=" + embedded
1144                               + " xclient=" + xclient);
1145             }
1146 
1147             /* Make a copy of the original event, since we are going to modify the
1148                event while it still can be referenced from other Java events. */
1149             {
1150                 XClientMessageEvent copy = new XClientMessageEvent();
1151                 unsafe.copyMemory(xclient.pData, copy.pData, XClientMessageEvent.getSize());
1152 
1153                 copy.set_data(0, xclient.get_window());
1154 
1155                 forwardClientMessageToToplevel(embedded, copy);
1156             }
1157         }
1158 
1159         if (eventID == MouseEvent.MOUSE_EXITED) {
1160             if (overXEmbedClient) {
1161                 if (ctxt != 0 || prevCtxt != 0) {
1162                     // Last chance to send XdndLeave to the XEmbed client.
1163                     XClientMessageEvent xclient = ctxt != 0 ?
1164                         new XClientMessageEvent(ctxt) :
1165                         new XClientMessageEvent(prevCtxt);
1166                     sendLeaveMessageToToplevelImpl(embedded, xclient.get_window());
1167                 }
1168                 overXEmbedClient = false;
1169                 // We have to clear the proxy mode source window here,
1170                 // when the drag exits the XEmbedCanvasPeer.
1171                 // NOTE: at this point the XEmbed client still might have some
1172                 // drag notifications to process and it will send responses to
1173                 // us. With the proxy mode source window cleared we won't be
1174                 // able to forward these responses to the actual source. This is
1175                 // not a problem if the drag operation was initiated in this
1176                 // JVM. However, if it was initiated in another processes the
1177                 // responses will be lost. We bear with it for now, as it seems
1178                 // there is no other reliable point to clear.
1179                 XDragSourceContextPeer.setProxyModeSourceWindow(0);
1180             }
1181         }
1182 
1183         if (eventID == MouseEvent.MOUSE_RELEASED) {
1184             overXEmbedClient = false;
1185             cleanup();
1186         }
1187 
1188         if (prevCtxt != 0) {
1189             unsafe.freeMemory(prevCtxt);
1190             prevCtxt = 0;
1191         }
1192 
1193         if (ctxt != 0 && overXEmbedClient) {
1194             prevCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1195 
1196             unsafe.copyMemory(ctxt, prevCtxt, size + 4 * Native.getLongSize());
1197         }
1198 
1199         return true;
1200     }
1201 
isXEmbedSupported()1202     public boolean isXEmbedSupported() {
1203         return true;
1204     }
1205 }
1206