1 /*
   2  * Copyright (c) 2003, 2013, 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.util.logging.PlatformLogger;
  36 
  37 import sun.misc.Unsafe;
  38 
  39 /**
  40  * XDragSourceProtocol implementation for XDnD protocol.
  41  *
  42  * @since 1.5
  43  */
  44 class XDnDDragSourceProtocol extends XDragSourceProtocol {
  45     private static final PlatformLogger logger =
  46         PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDragSourceProtocol");
  47 
  48     private static final Unsafe unsafe = XlibWrapper.unsafe;
  49 
  50     protected XDnDDragSourceProtocol(XDragSourceProtocolListener listener) {
  51         super(listener);
  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 XDnDDragSourceProtocol(listener);
  61     }
  62 
  63     public String getProtocolName() {
  64         return XDragAndDropProtocols.XDnD;
  65     }
  66 
  67     /**
  68      * Performs protocol-specific drag initialization.
  69      *
  70      * @returns true if the initialized successfully.
  71      */
  72     protected void initializeDragImpl(int actions, Transferable contents,
  73                                          Map formatMap, long[] formats)
  74       throws InvalidDnDOperationException,
  75         IllegalArgumentException, XException {
  76         assert XToolkit.isAWTLockHeldByCurrentThread();
  77 
  78         long window = XDragSourceProtocol.getDragSourceWindow();
  79 
  80         long data = Native.allocateLongArray(3);
  81         int action_count = 0;
  82         try {
  83             if ((actions & DnDConstants.ACTION_COPY) != 0) {
  84                 Native.putLong(data, action_count,
  85                                XDnDConstants.XA_XdndActionCopy.getAtom());
  86                 action_count++;
  87             }
  88             if ((actions & DnDConstants.ACTION_MOVE) != 0) {
  89                 Native.putLong(data, action_count,
  90                                XDnDConstants.XA_XdndActionMove.getAtom());
  91                 action_count++;
  92             }
  93             if ((actions & DnDConstants.ACTION_LINK) != 0) {
  94                 Native.putLong(data, action_count,
  95                                XDnDConstants.XA_XdndActionLink.getAtom());
  96                 action_count++;
  97             }
  98 
  99             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 100             XDnDConstants.XA_XdndActionList.setAtomData(window,
 101                                                         XAtom.XA_ATOM,
 102                                                         data, action_count);
 103             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 104 
 105             if ((XErrorHandlerUtil.saved_error) != null &&
 106                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 107                 cleanup();
 108                 throw new XException("Cannot write XdndActionList property");
 109             }
 110         } finally {
 111             unsafe.freeMemory(data);
 112             data = 0;
 113         }
 114 
 115         data = Native.allocateLongArray(formats.length);
 116 
 117         try {
 118             Native.put(data, formats);
 119 
 120             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 121             XDnDConstants.XA_XdndTypeList.setAtomData(window,
 122                                                       XAtom.XA_ATOM,
 123                                                       data, formats.length);
 124             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 125 
 126             if ((XErrorHandlerUtil.saved_error != null) &&
 127                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 128                 cleanup();
 129                 throw new XException("Cannot write XdndActionList property");
 130             }
 131         } finally {
 132             unsafe.freeMemory(data);
 133             data = 0;
 134         }
 135 
 136         if (!XDnDConstants.XDnDSelection.setOwner(contents, formatMap, formats,
 137                                                   XConstants.CurrentTime)) {
 138             cleanup();
 139             throw new InvalidDnDOperationException("Cannot acquire selection ownership");
 140         }
 141     }
 142 
 143     private boolean processXdndStatus(XClientMessageEvent xclient) {
 144         int action = DnDConstants.ACTION_NONE;
 145 
 146         /* Ignore XDnD messages from all other windows. */
 147         if (xclient.get_data(0) != getTargetWindow()) {
 148             return true;
 149         }
 150 
 151         if ((xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0) {
 152             /* This feature is new in XDnD version 2, but we can use it as XDnD
 153                compliance only requires supporting version 3 and up. */
 154             action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(4));
 155         }
 156 
 157         getProtocolListener().handleDragReply(action);
 158 
 159         return true;
 160     }
 161 
 162     private boolean processXdndFinished(XClientMessageEvent xclient) {
 163         /* Ignore XDnD messages from all other windows. */
 164         if (xclient.get_data(0) != getTargetWindow()) {
 165             return true;
 166         }
 167 
 168         if (getTargetProtocolVersion() >= 5) {
 169             boolean success = (xclient.get_data(1) & XDnDConstants.XDND_ACCEPT_DROP_FLAG) != 0;
 170             int action = XDnDConstants.getJavaActionForXDnDAction(xclient.get_data(2));
 171             getProtocolListener().handleDragFinished(success, action);
 172         } else {
 173             getProtocolListener().handleDragFinished();
 174         }
 175 
 176         finalizeDrop();
 177 
 178         return true;
 179     }
 180 
 181     public boolean processClientMessage(XClientMessageEvent xclient) {
 182         if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom()) {
 183             return processXdndStatus(xclient);
 184         } else if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) {
 185             return processXdndFinished(xclient);
 186         } else {
 187             return false;
 188         }
 189     }
 190 
 191     public TargetWindowInfo getTargetWindowInfo(long window) {
 192         assert XToolkit.isAWTLockHeldByCurrentThread();
 193 
 194         WindowPropertyGetter wpg1 =
 195             new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1,
 196                                      false, XConstants.AnyPropertyType);
 197 
 198         int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 199 
 200         if (status == XConstants.Success &&
 201             wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
 202 
 203             int targetVersion = (int)Native.getLong(wpg1.getData());
 204 
 205             wpg1.dispose();
 206 
 207             if (targetVersion >= XDnDConstants.XDND_MIN_PROTOCOL_VERSION) {
 208                 long proxy = 0;
 209                 int protocolVersion =
 210                     targetVersion < XDnDConstants.XDND_PROTOCOL_VERSION ?
 211                     targetVersion : XDnDConstants.XDND_PROTOCOL_VERSION;
 212 
 213                 WindowPropertyGetter wpg2 =
 214                     new WindowPropertyGetter(window, XDnDConstants.XA_XdndProxy,
 215                                              0, 1, false, XAtom.XA_WINDOW);
 216 
 217                 try {
 218                     status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 219 
 220                     if (status == XConstants.Success &&
 221                         wpg2.getData() != 0 &&
 222                         wpg2.getActualType() == XAtom.XA_WINDOW) {
 223 
 224                         proxy = Native.getLong(wpg2.getData());
 225                     }
 226                 } finally {
 227                     wpg2.dispose();
 228                 }
 229 
 230                 if (proxy != 0) {
 231                     WindowPropertyGetter wpg3 =
 232                         new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
 233                                                  0, 1, false, XAtom.XA_WINDOW);
 234 
 235                     try {
 236                         status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 237 
 238                         if (status != XConstants.Success ||
 239                             wpg3.getData() == 0 ||
 240                             wpg3.getActualType() != XAtom.XA_WINDOW ||
 241                             Native.getLong(wpg3.getData()) != proxy) {
 242 
 243                             proxy = 0;
 244                         } else {
 245                             WindowPropertyGetter wpg4 =
 246                                 new WindowPropertyGetter(proxy,
 247                                                          XDnDConstants.XA_XdndAware,
 248                                                          0, 1, false,
 249                                                          XConstants.AnyPropertyType);
 250 
 251                             try {
 252                                 status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 253 
 254                                 if (status != XConstants.Success ||
 255                                     wpg4.getData() == 0 ||
 256                                     wpg4.getActualType() != XAtom.XA_ATOM) {
 257 
 258                                     proxy = 0;
 259                                 }
 260                             } finally {
 261                                 wpg4.dispose();
 262                             }
 263                         }
 264                     } finally {
 265                         wpg3.dispose();
 266                     }
 267                 }
 268 
 269                 return new TargetWindowInfo(proxy, protocolVersion);
 270             }
 271         } else {
 272             wpg1.dispose();
 273         }
 274 
 275         return null;
 276     }
 277 
 278     public void sendEnterMessage(long[] formats,
 279                                  int sourceAction, int sourceActions, long time) {
 280         assert XToolkit.isAWTLockHeldByCurrentThread();
 281         assert getTargetWindow() != 0;
 282         assert formats != null;
 283 
 284         XClientMessageEvent msg = new XClientMessageEvent();
 285         try {
 286             msg.set_type((int)XConstants.ClientMessage);
 287             msg.set_window(getTargetWindow());
 288             msg.set_format(32);
 289             msg.set_message_type(XDnDConstants.XA_XdndEnter.getAtom());
 290             msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
 291             long data1 =
 292                 getTargetProtocolVersion() << XDnDConstants.XDND_PROTOCOL_SHIFT;
 293             data1 |= formats.length > 3 ? XDnDConstants.XDND_DATA_TYPES_BIT : 0;
 294             msg.set_data(1, data1);
 295             msg.set_data(2, formats.length > 0 ? formats[0] : 0);
 296             msg.set_data(3, formats.length > 1 ? formats[1] : 0);
 297             msg.set_data(4, formats.length > 2 ? formats[2] : 0);
 298             XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 299                                    getTargetProxyWindow(),
 300                                    false, XConstants.NoEventMask,
 301                                    msg.pData);
 302         } finally {
 303             msg.dispose();
 304         }
 305     }
 306 
 307     public void sendMoveMessage(int xRoot, int yRoot,
 308                                 int sourceAction, int sourceActions, long time) {
 309         assert XToolkit.isAWTLockHeldByCurrentThread();
 310         assert getTargetWindow() != 0;
 311 
 312         XClientMessageEvent msg = new XClientMessageEvent();
 313         try {
 314             msg.set_type((int)XConstants.ClientMessage);
 315             msg.set_window(getTargetWindow());
 316             msg.set_format(32);
 317             msg.set_message_type(XDnDConstants.XA_XdndPosition.getAtom());
 318             msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
 319             msg.set_data(1, 0); /* flags */
 320             msg.set_data(2, xRoot << 16 | yRoot);
 321             msg.set_data(3, time);
 322             msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(sourceAction));
 323             XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 324                                    getTargetProxyWindow(),
 325                                    false, XConstants.NoEventMask,
 326                                    msg.pData);
 327         } finally {
 328             msg.dispose();
 329         }
 330     }
 331 
 332     public void sendLeaveMessage(long time) {
 333         assert XToolkit.isAWTLockHeldByCurrentThread();
 334         assert getTargetWindow() != 0;
 335 
 336         XClientMessageEvent msg = new XClientMessageEvent();
 337         try {
 338             msg.set_type((int)XConstants.ClientMessage);
 339             msg.set_window(getTargetWindow());
 340             msg.set_format(32);
 341             msg.set_message_type(XDnDConstants.XA_XdndLeave.getAtom());
 342             msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
 343             msg.set_data(1, 0);
 344             msg.set_data(2, 0);
 345             msg.set_data(3, 0);
 346             msg.set_data(4, 0);
 347             XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 348                                    getTargetProxyWindow(),
 349                                    false, XConstants.NoEventMask,
 350                                    msg.pData);
 351         } finally {
 352             msg.dispose();
 353         }
 354     }
 355 
 356     public void sendDropMessage(int xRoot, int yRoot,
 357                                 int sourceAction, int sourceActions,
 358                                 long time) {
 359         assert XToolkit.isAWTLockHeldByCurrentThread();
 360         assert getTargetWindow() != 0;
 361 
 362         XClientMessageEvent msg = new XClientMessageEvent();
 363         try {
 364             msg.set_type((int)XConstants.ClientMessage);
 365             msg.set_window(getTargetWindow());
 366             msg.set_format(32);
 367             msg.set_message_type(XDnDConstants.XA_XdndDrop.getAtom());
 368             msg.set_data(0, XDragSourceProtocol.getDragSourceWindow());
 369             msg.set_data(1, 0); /* flags */
 370             msg.set_data(2, time);
 371             msg.set_data(3, 0);
 372             msg.set_data(4, 0);
 373             XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 374                                    getTargetProxyWindow(),
 375                                    false, XConstants.NoEventMask,
 376                                    msg.pData);
 377         } finally {
 378             msg.dispose();
 379         }
 380     }
 381 
 382     public boolean processProxyModeEvent(XClientMessageEvent xclient,
 383                                          long sourceWindow) {
 384         if (xclient.get_message_type() == XDnDConstants.XA_XdndStatus.getAtom() ||
 385             xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) {
 386 
 387             if (xclient.get_message_type() == XDnDConstants.XA_XdndFinished.getAtom()) {
 388                 XDragSourceContextPeer.setProxyModeSourceWindow(0);
 389             }
 390 
 391             // This can happen if the drag operation started in the XEmbed server.
 392             // In this case there is no need to forward it elsewhere, we should
 393             // process it here.
 394             if (xclient.get_window() == sourceWindow) {
 395                 return false;
 396             }
 397 
 398             if (logger.isLoggable(PlatformLogger.FINEST)) {
 399                 logger.finest("        sourceWindow=" + sourceWindow +
 400                               " get_window=" + xclient.get_window() +
 401                               " xclient=" + xclient);
 402             }
 403             xclient.set_data(0, xclient.get_window());
 404             xclient.set_window(sourceWindow);
 405 
 406             assert XToolkit.isAWTLockHeldByCurrentThread();
 407 
 408             XlibWrapper.XSendEvent(XToolkit.getDisplay(), sourceWindow,
 409                                    false, XConstants.NoEventMask,
 410                                    xclient.pData);
 411 
 412             return true;
 413         }
 414 
 415         return false;
 416     }
 417 
 418     // TODO: register this runnable with XDnDSelection.
 419     public void run() {
 420         // XdndSelection ownership lost.
 421         cleanup();
 422     }
 423 }