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 sun.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
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 }
--- EOF ---