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