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.datatransfer.Transferable;
29 import java.awt.datatransfer.DataFlavor;
30 
31 import java.awt.dnd.DnDConstants;
32 import java.awt.dnd.InvalidDnDOperationException;
33 
34 import java.util.Map;
35 
36 import jdk.internal.misc.Unsafe;
37 
38 /**
39  * XDragSourceProtocol implementation for Motif DnD protocol.
40  *
41  * @since 1.5
42  */
43 class MotifDnDDragSourceProtocol extends XDragSourceProtocol
44     implements XEventDispatcher {
45 
46     private static final Unsafe unsafe = XlibWrapper.unsafe;
47 
48     private long targetEnterServerTime = XConstants.CurrentTime;
49 
MotifDnDDragSourceProtocol(XDragSourceProtocolListener listener)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      */
createInstance(XDragSourceProtocolListener listener)60     static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) {
61         return new MotifDnDDragSourceProtocol(listener);
62     }
63 
getProtocolName()64     public String getProtocolName() {
65         return XDragAndDropProtocols.MotifDnD;
66     }
67 
initializeDragImpl(int actions, Transferable contents, Map<Long, DataFlavor> formatMap, long[] formats)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      */
processClientMessage(XClientMessageEvent xclient)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 
getTargetWindowInfo(long window)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 
sendEnterMessage(long[] formats, int sourceAction, int sourceActions, long time)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 
sendMoveMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)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 
sendLeaveMessage(long time)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 
sendDropMessage(int xRoot, int yRoot, int sourceAction, int sourceActions, long time)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 
processProxyModeEvent(XClientMessageEvent xclient, long sourceWindow)394     public boolean processProxyModeEvent(XClientMessageEvent xclient,
395                                          long sourceWindow) {
396         // Motif DnD for XEmbed is not implemented.
397         return false;
398     }
399 
cleanupTargetInfo()400     public void cleanupTargetInfo() {
401         super.cleanupTargetInfo();
402         targetEnterServerTime = XConstants.CurrentTime;
403     }
404 
dispatchEvent(XEvent ev)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