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.datatransfer.Transferable;
29import java.awt.datatransfer.DataFlavor;
30
31import java.awt.dnd.DnDConstants;
32import java.awt.dnd.InvalidDnDOperationException;
33
34import java.util.Map;
35
36import jdk.internal.misc.Unsafe;
37
38/**
39 * XDragSourceProtocol implementation for Motif DnD protocol.
40 *
41 * @since 1.5
42 */
43class MotifDnDDragSourceProtocol extends XDragSourceProtocol
44    implements XEventDispatcher {
45
46    private static final Unsafe unsafe = XlibWrapper.unsafe;
47
48    private long targetEnterServerTime = XConstants.CurrentTime;
49
50    protected MotifDnDDragSourceProtocol(XDragSourceProtocolListener listener) {
51        super(listener);
52        XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), this);
53    }
54
55    /**
56     * Creates an instance associated with the specified listener.
57     *
58     * @throws NullPointerException if listener is {@code null}.
59     */
60    static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) {
61        return new MotifDnDDragSourceProtocol(listener);
62    }
63
64    public String getProtocolName() {
65        return XDragAndDropProtocols.MotifDnD;
66    }
67
68    protected void initializeDragImpl(int actions, Transferable contents,
69                                      Map<Long, DataFlavor> formatMap, long[] formats)
70      throws InvalidDnDOperationException,
71        IllegalArgumentException, XException {
72
73        long window = XDragSourceProtocol.getDragSourceWindow();
74
75        /* Write the Motif DnD initiator info on the root XWindow. */
76        try {
77            int index = MotifDnDConstants.getIndexForTargetList(formats);
78
79            MotifDnDConstants.writeDragInitiatorInfoStruct(window, index);
80        } catch (XException xe) {
81            cleanup();
82            throw xe;
83        } catch (InvalidDnDOperationException idoe) {
84            cleanup();
85            throw idoe;
86        }
87
88        if (!MotifDnDConstants.MotifDnDSelection.setOwner(contents, formatMap,
89                                                          formats,
90                                                          XConstants.CurrentTime)) {
91            cleanup();
92            throw new InvalidDnDOperationException("Cannot acquire selection ownership");
93        }
94    }
95
96    /**
97     * Processes the specified client message event.
98     *
99     * @return true if the event was successfully processed.
100     */
101    public boolean processClientMessage(XClientMessageEvent xclient) {
102        if (xclient.get_message_type() !=
103            MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
104            return false;
105        }
106
107        long data = xclient.get_data();
108        byte reason = (byte)(unsafe.getByte(data) &
109            MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
110        byte origin = (byte)(unsafe.getByte(data) &
111            MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
112        byte byteOrder = unsafe.getByte(data + 1);
113        boolean swapNeeded = byteOrder != MotifDnDConstants.getByteOrderByte();
114        int action = DnDConstants.ACTION_NONE;
115        int x = 0;
116        int y = 0;
117
118        /* Only receiver messages should be handled. */
119        if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_RECEIVER) {
120            return false;
121        }
122
123        switch (reason) {
124        case MotifDnDConstants.DROP_SITE_ENTER:
125        case MotifDnDConstants.DROP_SITE_LEAVE:
126        case MotifDnDConstants.DRAG_MOTION:
127        case MotifDnDConstants.OPERATION_CHANGED:
128            break;
129        default:
130            // Unknown reason.
131            return false;
132        }
133
134        int t = unsafe.getInt(data + 4);
135        if (swapNeeded) {
136            t = MotifDnDConstants.Swapper.swap(t);
137        }
138        long time = t & 0xffffffffL;
139             // with correction of (32-bit unsigned to 64-bit signed) implicit conversion.
140
141        /* Discard events from the previous receiver. */
142        if (targetEnterServerTime == XConstants.CurrentTime ||
143            time < targetEnterServerTime) {
144            return true;
145        }
146
147        if (reason != MotifDnDConstants.DROP_SITE_LEAVE) {
148            short flags = unsafe.getShort(data + 2);
149            if (swapNeeded) {
150                flags = MotifDnDConstants.Swapper.swap(flags);
151            }
152
153            byte status = (byte)((flags & MotifDnDConstants.MOTIF_DND_STATUS_MASK) >>
154                MotifDnDConstants.MOTIF_DND_STATUS_SHIFT);
155            byte motif_action = (byte)((flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >>
156                MotifDnDConstants.MOTIF_DND_ACTION_SHIFT);
157
158            if (status == MotifDnDConstants.MOTIF_VALID_DROP_SITE) {
159                action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action);
160            } else {
161                action = DnDConstants.ACTION_NONE;
162            }
163
164            short tx = unsafe.getShort(data + 8);
165            short ty = unsafe.getShort(data + 10);
166            if (swapNeeded) {
167                tx = MotifDnDConstants.Swapper.swap(tx);
168                ty = MotifDnDConstants.Swapper.swap(ty);
169            }
170            x = tx;
171            y = ty;
172        }
173
174        getProtocolListener().handleDragReply(action, x, y);
175
176        return true;
177    }
178
179    public TargetWindowInfo getTargetWindowInfo(long window) {
180        assert XToolkit.isAWTLockHeldByCurrentThread();
181
182        WindowPropertyGetter wpg =
183            new WindowPropertyGetter(window,
184                                     MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
185                                     0, 0xFFFF, false,
186                                     XConstants.AnyPropertyType);
187
188        try {
189            int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
190
191            /*
192             * DragICCI.h:
193             *
194             * typedef struct _xmDragReceiverInfoStruct{
195             *     BYTE byte_order;
196             *     BYTE protocol_version;
197             *     BYTE drag_protocol_style;
198             *     BYTE pad1;
199             *     CARD32       proxy_window B32;
200             *     CARD16       num_drop_sites B16;
201             *     CARD16       pad2 B16;
202             *     CARD32       heap_offset B32;
203             * } xmDragReceiverInfoStruct;
204             */
205            if (status == XConstants.Success && wpg.getData() != 0 &&
206                wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
207                wpg.getNumberOfItems() >=
208                MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
209
210                long data = wpg.getData();
211                byte byteOrderByte = unsafe.getByte(data);
212                byte dragProtocolStyle = unsafe.getByte(data + 2);
213                switch (dragProtocolStyle) {
214                case MotifDnDConstants.MOTIF_PREFER_PREREGISTER_STYLE :
215                case MotifDnDConstants.MOTIF_PREFER_DYNAMIC_STYLE :
216                case MotifDnDConstants.MOTIF_DYNAMIC_STYLE :
217                case MotifDnDConstants.MOTIF_PREFER_RECEIVER_STYLE :
218                    int proxy = unsafe.getInt(data + 4);
219                    if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
220                        proxy = MotifDnDConstants.Swapper.swap(proxy);
221                    }
222
223                    int protocolVersion = unsafe.getByte(data + 1);
224
225                    return new TargetWindowInfo(proxy, protocolVersion);
226                default:
227                    // Unsupported protocol style.
228                    return null;
229                }
230            } else {
231                return null;
232            }
233        } finally {
234            wpg.dispose();
235        }
236    }
237
238    public void sendEnterMessage(long[] formats,
239                                 int sourceAction, int sourceActions, long time) {
240        assert XToolkit.isAWTLockHeldByCurrentThread();
241        assert getTargetWindow() != 0;
242        assert formats != null;
243
244        targetEnterServerTime = time;
245
246        XClientMessageEvent msg = new XClientMessageEvent();
247        try {
248            msg.set_type(XConstants.ClientMessage);
249            msg.set_window(getTargetWindow());
250            msg.set_format(8);
251            msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
252
253            long data = msg.get_data();
254            int flags =
255                (MotifDnDConstants.getMotifActionsForJavaActions(sourceAction) <<
256                 MotifDnDConstants.MOTIF_DND_ACTION_SHIFT) |
257                (MotifDnDConstants.getMotifActionsForJavaActions(sourceActions) <<
258                 MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT);
259
260            unsafe.putByte(data,
261                           (byte)(MotifDnDConstants.TOP_LEVEL_ENTER |
262                                  MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
263            unsafe.putByte(data + 1,
264                           MotifDnDConstants.getByteOrderByte());
265            unsafe.putShort(data + 2, (short)flags);
266            unsafe.putInt(data + 4, (int)time);
267            unsafe.putInt(data + 8, (int)XDragSourceProtocol.getDragSourceWindow());
268            unsafe.putInt(data + 12, (int)MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom());
269
270            XlibWrapper.XSendEvent(XToolkit.getDisplay(),
271                                   getTargetProxyWindow(),
272                                   false, XConstants.NoEventMask,
273                                   msg.pData);
274        } finally {
275            msg.dispose();
276        }
277    }
278
279    public void sendMoveMessage(int xRoot, int yRoot,
280                                int sourceAction, int sourceActions, long time) {
281        assert XToolkit.isAWTLockHeldByCurrentThread();
282        assert getTargetWindow() != 0;
283
284        XClientMessageEvent msg = new XClientMessageEvent();
285        try {
286            msg.set_type(XConstants.ClientMessage);
287            msg.set_window(getTargetWindow());
288            msg.set_format(8);
289            msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
290
291            long data = msg.get_data();
292            int flags =
293                (MotifDnDConstants.getMotifActionsForJavaActions(sourceAction) <<
294                 MotifDnDConstants.MOTIF_DND_ACTION_SHIFT) |
295                (MotifDnDConstants.getMotifActionsForJavaActions(sourceActions) <<
296                 MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT);
297
298            unsafe.putByte(data,
299                           (byte)(MotifDnDConstants.DRAG_MOTION |
300                                  MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
301            unsafe.putByte(data + 1,
302                           MotifDnDConstants.getByteOrderByte());
303            unsafe.putShort(data + 2, (short)flags);
304            unsafe.putInt(data + 4, (int)time);
305            unsafe.putShort(data + 8, (short)xRoot);
306            unsafe.putShort(data + 10, (short)yRoot);
307
308            XlibWrapper.XSendEvent(XToolkit.getDisplay(),
309                                   getTargetProxyWindow(),
310                                   false, XConstants.NoEventMask,
311                                   msg.pData);
312        } finally {
313            msg.dispose();
314        }
315    }
316
317    public void sendLeaveMessage(long time) {
318        assert XToolkit.isAWTLockHeldByCurrentThread();
319        assert getTargetWindow() != 0;
320
321        XClientMessageEvent msg = new XClientMessageEvent();
322        try {
323            msg.set_type(XConstants.ClientMessage);
324            msg.set_window(getTargetWindow());
325            msg.set_format(8);
326            msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
327
328            long data = msg.get_data();
329
330            unsafe.putByte(data,
331                           (byte)(MotifDnDConstants.TOP_LEVEL_LEAVE |
332                                  MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
333            unsafe.putByte(data + 1,
334                           MotifDnDConstants.getByteOrderByte());
335            unsafe.putShort(data + 2, (short)0);
336            unsafe.putInt(data + 4, (int)time);
337            unsafe.putInt(data + 8, (int)XDragSourceProtocol.getDragSourceWindow());
338
339            XlibWrapper.XSendEvent(XToolkit.getDisplay(),
340                                   getTargetProxyWindow(),
341                                   false, XConstants.NoEventMask,
342                                   msg.pData);
343        } finally {
344            msg.dispose();
345        }
346    }
347
348    protected void sendDropMessage(int xRoot, int yRoot,
349                                   int sourceAction, int sourceActions,
350                                   long time) {
351        assert XToolkit.isAWTLockHeldByCurrentThread();
352        assert getTargetWindow() != 0;
353
354        /*
355         * Motif drop sites expect TOP_LEVEL_LEAVE before DROP_START.
356         */
357        sendLeaveMessage(time);
358
359        XClientMessageEvent msg = new XClientMessageEvent();
360        try {
361            msg.set_type(XConstants.ClientMessage);
362            msg.set_window(getTargetWindow());
363            msg.set_format(8);
364            msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
365
366            long data = msg.get_data();
367            int flags =
368                (MotifDnDConstants.getMotifActionsForJavaActions(sourceAction) <<
369                 MotifDnDConstants.MOTIF_DND_ACTION_SHIFT) |
370                (MotifDnDConstants.getMotifActionsForJavaActions(sourceActions) <<
371                 MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT);
372
373            unsafe.putByte(data,
374                           (byte)(MotifDnDConstants.DROP_START |
375                                  MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
376            unsafe.putByte(data + 1,
377                           MotifDnDConstants.getByteOrderByte());
378            unsafe.putShort(data + 2, (short)flags);
379            unsafe.putInt(data + 4, (int)time);
380            unsafe.putShort(data + 8, (short)xRoot);
381            unsafe.putShort(data + 10, (short)yRoot);
382            unsafe.putInt(data + 12, (int)MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom());
383            unsafe.putInt(data + 16, (int)XDragSourceProtocol.getDragSourceWindow());
384
385            XlibWrapper.XSendEvent(XToolkit.getDisplay(),
386                                   getTargetProxyWindow(),
387                                   false, XConstants.NoEventMask,
388                                   msg.pData);
389        } finally {
390            msg.dispose();
391        }
392    }
393
394    public boolean processProxyModeEvent(XClientMessageEvent xclient,
395                                         long sourceWindow) {
396        // Motif DnD for XEmbed is not implemented.
397        return false;
398    }
399
400    public void cleanupTargetInfo() {
401        super.cleanupTargetInfo();
402        targetEnterServerTime = XConstants.CurrentTime;
403    }
404
405    public void dispatchEvent(XEvent ev) {
406        switch (ev.get_type()) {
407        case XConstants.SelectionRequest:
408            XSelectionRequestEvent xsre = ev.get_xselectionrequest();
409            long atom = xsre.get_selection();
410
411            if (atom == MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom()) {
412                long target = xsre.get_target();
413                if (target == MotifDnDConstants.XA_XmTRANSFER_SUCCESS.getAtom()) {
414                    getProtocolListener().handleDragFinished(true);
415                } else if (target == MotifDnDConstants.XA_XmTRANSFER_FAILURE.getAtom()) {
416                    getProtocolListener().handleDragFinished(false);
417                }
418            }
419            break;
420        }
421    }
422}
423