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 
  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 == 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 }