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</code>. 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 * @returns 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 }