1/*
2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.awt.X11;
27
28import java.awt.Point;
29
30import java.awt.dnd.DnDConstants;
31
32import java.awt.event.MouseEvent;
33
34import java.io.IOException;
35
36import sun.util.logging.PlatformLogger;
37
38import jdk.internal.misc.Unsafe;
39
40/**
41 * XDropTargetProtocol implementation for XDnD protocol.
42 *
43 * @since 1.5
44 */
45class 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
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     */
75    static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
76        return new XDnDDropTargetProtocol(listener);
77    }
78
79    public String getProtocolName() {
80        return XDragAndDropProtocols.XDnD;
81    }
82
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
105    public void unregisterDropTarget(long window) {
106        assert XToolkit.isAWTLockHeldByCurrentThread();
107
108        XDnDConstants.XA_XdndAware.DeleteProperty(window);
109    }
110
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
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     */
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
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
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
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);
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
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
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
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
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
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
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
762    protected void sendLeaveMessageToToplevel(long toplevel,
763                                              XClientMessageEvent xclient) {
764        sendLeaveMessageToToplevelImpl(toplevel, xclient.get_data(0));
765    }
766
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
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
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
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);
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
940    public final long getSourceWindow() {
941        return sourceWindow;
942    }
943
944    /**
945     * Reset the state of the object.
946     */
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
981    public boolean isDragOverComponent() {
982        return targetXWindow != null;
983    }
984
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
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
1202    public boolean isXEmbedSupported() {
1203        return true;
1204    }
1205}
1206