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