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 }