1 /*
   2  * Copyright (c) 2003, 2008, 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.Point;
  29 
  30 import java.awt.dnd.DnDConstants;
  31 
  32 import java.awt.event.MouseEvent;
  33 
  34 import java.io.IOException;
  35 
  36 import sun.util.logging.PlatformLogger;
  37 
  38 import sun.misc.Unsafe;
  39 
  40 /**
  41  * XDropTargetProtocol implementation for XDnD protocol.
  42  *
  43  * @since 1.5
  44  */
  45 class XDnDDropTargetProtocol extends XDropTargetProtocol {
  46     private static final PlatformLogger logger =
  47         PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDnDDropTargetProtocol");
  48 
  49     private static final Unsafe unsafe = XlibWrapper.unsafe;
  50 
  51     private long sourceWindow = 0;
  52     private long sourceWindowMask = 0;
  53     private int sourceProtocolVersion = 0;
  54     private int sourceActions = DnDConstants.ACTION_NONE;
  55     private long[] sourceFormats = null;
  56     private boolean trackSourceActions = false;
  57     private int userAction = DnDConstants.ACTION_NONE;
  58     private int sourceX = 0;
  59     private int sourceY = 0;
  60     private XWindow targetXWindow = null;
  61 
  62     // XEmbed stuff.
  63     private long prevCtxt = 0;
  64     private boolean overXEmbedClient = false;
  65 
  66     protected XDnDDropTargetProtocol(XDropTargetProtocolListener listener) {
  67         super(listener);
  68     }
  69 
  70     /**
  71      * Creates an instance associated with the specified listener.
  72      *
  73      * @throws NullPointerException if listener is <code>null</code>.
  74      */
  75     static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
  76         return new XDnDDropTargetProtocol(listener);
  77     }
  78 
  79     public String getProtocolName() {
  80         return XDragAndDropProtocols.XDnD;
  81     }
  82 
  83     public void registerDropTarget(long window) {
  84         assert XToolkit.isAWTLockHeldByCurrentThread();
  85 
  86         long data = Native.allocateLongArray(1);
  87 
  88         try {
  89             Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
  90 
  91             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
  92             XDnDConstants.XA_XdndAware.setAtomData(window, XAtom.XA_ATOM, data, 1);
  93             XToolkit.RESTORE_XERROR_HANDLER();
  94 
  95             if (XToolkit.saved_error != null &&
  96                 XToolkit.saved_error.get_error_code() != XConstants.Success) {
  97                 throw new XException("Cannot write XdndAware property");
  98             }
  99         } finally {
 100             unsafe.freeMemory(data);
 101             data = 0;
 102         }
 103     }
 104 
 105     public void unregisterDropTarget(long window) {
 106         assert XToolkit.isAWTLockHeldByCurrentThread();
 107 
 108         XDnDConstants.XA_XdndAware.DeleteProperty(window);
 109     }
 110 
 111     public void registerEmbedderDropSite(long embedder) {
 112         assert XToolkit.isAWTLockHeldByCurrentThread();
 113 
 114         boolean overriden = false;
 115         int version = 0;
 116         long proxy = 0;
 117         long newProxy = XDropTargetRegistry.getDnDProxyWindow();
 118         int status = 0;
 119 
 120         WindowPropertyGetter wpg1 =
 121             new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndAware, 0, 1,
 122                                      false, XConstants.AnyPropertyType);
 123 
 124         try {
 125             status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 126 
 127             if (status == XConstants.Success &&
 128                 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
 129 
 130                 overriden = true;
 131                 version = (int)Native.getLong(wpg1.getData());
 132             }
 133         } finally {
 134             wpg1.dispose();
 135         }
 136 
 137         /* XdndProxy is not supported for prior to XDnD version 4 */
 138         if (overriden && version >= 4) {
 139             WindowPropertyGetter wpg2 =
 140                 new WindowPropertyGetter(embedder, XDnDConstants.XA_XdndProxy,
 141                                          0, 1, false, XAtom.XA_WINDOW);
 142 
 143             try {
 144                 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 145 
 146                 if (status == XConstants.Success &&
 147                     wpg2.getData() != 0 &&
 148                     wpg2.getActualType() == XAtom.XA_WINDOW) {
 149 
 150                     proxy = Native.getLong(wpg2.getData());
 151                 }
 152             } finally {
 153                 wpg2.dispose();
 154             }
 155 
 156             if (proxy != 0) {
 157                 WindowPropertyGetter wpg3 =
 158                     new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
 159                                              0, 1, false, XAtom.XA_WINDOW);
 160 
 161                 try {
 162                     status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 163 
 164                     if (status != XConstants.Success ||
 165                         wpg3.getData() == 0 ||
 166                         wpg3.getActualType() != XAtom.XA_WINDOW ||
 167                         Native.getLong(wpg3.getData()) != proxy) {
 168 
 169                         proxy = 0;
 170                     } else {
 171                         WindowPropertyGetter wpg4 =
 172                             new WindowPropertyGetter(proxy,
 173                                                      XDnDConstants.XA_XdndAware,
 174                                                      0, 1, false,
 175                                                      XConstants.AnyPropertyType);
 176 
 177                         try {
 178                             status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 179 
 180                             if (status != XConstants.Success ||
 181                                 wpg4.getData() == 0 ||
 182                                 wpg4.getActualType() != XAtom.XA_ATOM) {
 183 
 184                                 proxy = 0;
 185                             }
 186                         } finally {
 187                             wpg4.dispose();
 188                         }
 189                     }
 190                 } finally {
 191                     wpg3.dispose();
 192                 }
 193             }
 194         }
 195 
 196         if (proxy == newProxy) {
 197             // Embedder already registered.
 198             return;
 199         }
 200 
 201         long data = Native.allocateLongArray(1);
 202 
 203         try {
 204             Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
 205 
 206             /* The proxy window must have the XdndAware set, as XDnD protocol
 207                prescribes to check the proxy window for XdndAware. */
 208             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 209             XDnDConstants.XA_XdndAware.setAtomData(newProxy, XAtom.XA_ATOM,
 210                                                    data, 1);
 211             XToolkit.RESTORE_XERROR_HANDLER();
 212 
 213             if (XToolkit.saved_error != null &&
 214                 XToolkit.saved_error.get_error_code() !=
 215                 XConstants.Success) {
 216                 throw new XException("Cannot write XdndAware property");
 217             }
 218 
 219             Native.putLong(data, 0, newProxy);
 220 
 221             /* The proxy window must have the XdndProxy set to point to itself.*/
 222             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 223             XDnDConstants.XA_XdndProxy.setAtomData(newProxy, XAtom.XA_WINDOW,
 224                                                    data, 1);
 225             XToolkit.RESTORE_XERROR_HANDLER();
 226 
 227             if (XToolkit.saved_error != null &&
 228                 XToolkit.saved_error.get_error_code() !=
 229                 XConstants.Success) {
 230                 throw new XException("Cannot write XdndProxy property");
 231             }
 232 
 233             Native.putLong(data, 0, XDnDConstants.XDND_PROTOCOL_VERSION);
 234 
 235             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 236             XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM,
 237                                                    data, 1);
 238             XToolkit.RESTORE_XERROR_HANDLER();
 239 
 240             if (XToolkit.saved_error != null &&
 241                 XToolkit.saved_error.get_error_code() !=
 242                 XConstants.Success) {
 243                 throw new XException("Cannot write XdndAware property");
 244             }
 245 
 246             Native.putLong(data, 0, newProxy);
 247 
 248             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 249             XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW,
 250                                                    data, 1);
 251             XToolkit.RESTORE_XERROR_HANDLER();
 252 
 253             if (XToolkit.saved_error != null &&
 254                 XToolkit.saved_error.get_error_code() !=
 255                 XConstants.Success) {
 256                 throw new XException("Cannot write XdndProxy property");
 257             }
 258         } finally {
 259             unsafe.freeMemory(data);
 260             data = 0;
 261         }
 262 
 263         putEmbedderRegistryEntry(embedder, overriden, version, proxy);
 264     }
 265 
 266     public void unregisterEmbedderDropSite(long embedder) {
 267         assert XToolkit.isAWTLockHeldByCurrentThread();
 268 
 269         EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder);
 270 
 271         if (entry == null) {
 272             return;
 273         }
 274 
 275         if (entry.isOverriden()) {
 276             long data = Native.allocateLongArray(1);
 277 
 278             try {
 279                 Native.putLong(data, 0, entry.getVersion());
 280 
 281                 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 282                 XDnDConstants.XA_XdndAware.setAtomData(embedder, XAtom.XA_ATOM,
 283                                                        data, 1);
 284                 XToolkit.RESTORE_XERROR_HANDLER();
 285 
 286                 if (XToolkit.saved_error != null &&
 287                     XToolkit.saved_error.get_error_code() !=
 288                     XConstants.Success) {
 289                     throw new XException("Cannot write XdndAware property");
 290                 }
 291 
 292                 Native.putLong(data, 0, (int)entry.getProxy());
 293 
 294                 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 295                 XDnDConstants.XA_XdndProxy.setAtomData(embedder, XAtom.XA_WINDOW,
 296                                                        data, 1);
 297                 XToolkit.RESTORE_XERROR_HANDLER();
 298 
 299                 if (XToolkit.saved_error != null &&
 300                     XToolkit.saved_error.get_error_code() !=
 301                     XConstants.Success) {
 302                     throw new XException("Cannot write XdndProxy property");
 303                 }
 304             } finally {
 305                 unsafe.freeMemory(data);
 306                 data = 0;
 307             }
 308         } else {
 309             XDnDConstants.XA_XdndAware.DeleteProperty(embedder);
 310             XDnDConstants.XA_XdndProxy.DeleteProperty(embedder);
 311         }
 312     }
 313 
 314     /*
 315      * Gets and stores in the registry the embedder's XDnD drop site info
 316      * from the embedded.
 317      */
 318     public void registerEmbeddedDropSite(long embedded) {
 319         assert XToolkit.isAWTLockHeldByCurrentThread();
 320 
 321         boolean overriden = false;
 322         int version = 0;
 323         long proxy = 0;
 324         long newProxy = XDropTargetRegistry.getDnDProxyWindow();
 325         int status = 0;
 326 
 327         WindowPropertyGetter wpg1 =
 328             new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndAware, 0, 1,
 329                                      false, XConstants.AnyPropertyType);
 330 
 331         try {
 332             status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 333 
 334             if (status == XConstants.Success &&
 335                 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
 336 
 337                 overriden = true;
 338                 version = (int)Native.getLong(wpg1.getData());
 339             }
 340         } finally {
 341             wpg1.dispose();
 342         }
 343 
 344         /* XdndProxy is not supported for prior to XDnD version 4 */
 345         if (overriden && version >= 4) {
 346             WindowPropertyGetter wpg2 =
 347                 new WindowPropertyGetter(embedded, XDnDConstants.XA_XdndProxy,
 348                                          0, 1, false, XAtom.XA_WINDOW);
 349 
 350             try {
 351                 status = wpg2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 352 
 353                 if (status == XConstants.Success &&
 354                     wpg2.getData() != 0 &&
 355                     wpg2.getActualType() == XAtom.XA_WINDOW) {
 356 
 357                     proxy = Native.getLong(wpg2.getData());
 358                 }
 359             } finally {
 360                 wpg2.dispose();
 361             }
 362 
 363             if (proxy != 0) {
 364                 WindowPropertyGetter wpg3 =
 365                     new WindowPropertyGetter(proxy, XDnDConstants.XA_XdndProxy,
 366                                              0, 1, false, XAtom.XA_WINDOW);
 367 
 368                 try {
 369                     status = wpg3.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 370 
 371                     if (status != XConstants.Success ||
 372                         wpg3.getData() == 0 ||
 373                         wpg3.getActualType() != XAtom.XA_WINDOW ||
 374                         Native.getLong(wpg3.getData()) != proxy) {
 375 
 376                         proxy = 0;
 377                     } else {
 378                         WindowPropertyGetter wpg4 =
 379                             new WindowPropertyGetter(proxy,
 380                                                      XDnDConstants.XA_XdndAware,
 381                                                      0, 1, false,
 382                                                      XConstants.AnyPropertyType);
 383 
 384                         try {
 385                             status = wpg4.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 386 
 387                             if (status != XConstants.Success ||
 388                                 wpg4.getData() == 0 ||
 389                                 wpg4.getActualType() != XAtom.XA_ATOM) {
 390 
 391                                 proxy = 0;
 392                             }
 393                         } finally {
 394                             wpg4.dispose();
 395                         }
 396                     }
 397                 } finally {
 398                     wpg3.dispose();
 399                 }
 400             }
 401         }
 402 
 403         putEmbedderRegistryEntry(embedded, overriden, version, proxy);
 404     }
 405 
 406     public boolean isProtocolSupported(long window) {
 407         assert XToolkit.isAWTLockHeldByCurrentThread();
 408 
 409         WindowPropertyGetter wpg1 =
 410             new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1,
 411                                      false, XConstants.AnyPropertyType);
 412 
 413         try {
 414             int status = wpg1.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 415 
 416             if (status == XConstants.Success &&
 417                 wpg1.getData() != 0 && wpg1.getActualType() == XAtom.XA_ATOM) {
 418 
 419                 return true;
 420             } else {
 421                 return false;
 422             }
 423         } finally {
 424             wpg1.dispose();
 425         }
 426     }
 427 
 428     private boolean processXdndEnter(XClientMessageEvent xclient) {
 429         long source_win = 0;
 430         long source_win_mask = 0;
 431         int protocol_version = 0;
 432         int actions = DnDConstants.ACTION_NONE;
 433         boolean track = true;
 434         long[] formats = null;
 435 
 436         if (getSourceWindow() != 0) {
 437             return false;
 438         }
 439 
 440         if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow)
 441             && getEmbedderRegistryEntry(xclient.get_window()) == null) {
 442             return false;
 443         }
 444 
 445         if (xclient.get_message_type() != XDnDConstants.XA_XdndEnter.getAtom()){
 446             return false;
 447         }
 448 
 449         protocol_version =
 450             (int)((xclient.get_data(1) & XDnDConstants.XDND_PROTOCOL_MASK) >>
 451                   XDnDConstants.XDND_PROTOCOL_SHIFT);
 452 
 453         /* XDnD compliance only requires supporting version 3 and up. */
 454         if (protocol_version < XDnDConstants.XDND_MIN_PROTOCOL_VERSION) {
 455             return false;
 456         }
 457 
 458         /* Ignore the source if the protocol version is higher than we support. */
 459         if (protocol_version > XDnDConstants.XDND_PROTOCOL_VERSION) {
 460             return false;
 461         }
 462 
 463         source_win = xclient.get_data(0);
 464 
 465         /* Extract the list of supported actions. */
 466         if (protocol_version < 2) {
 467             /* Prior to XDnD version 2 only COPY action was supported. */
 468             actions = DnDConstants.ACTION_COPY;
 469         } else {
 470             WindowPropertyGetter wpg =
 471                 new WindowPropertyGetter(source_win,
 472                                          XDnDConstants.XA_XdndActionList,
 473                                          0, 0xFFFF, false,
 474                                          XAtom.XA_ATOM);
 475             try {
 476                 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 477 
 478                 if (wpg.getActualType() == XAtom.XA_ATOM &&
 479                     wpg.getActualFormat() == 32) {
 480                     long data = wpg.getData();
 481 
 482                     for (int i = 0; i < wpg.getNumberOfItems(); i++) {
 483                         actions |=
 484                             XDnDConstants.getJavaActionForXDnDAction(Native.getLong(data, i));
 485                     }
 486                 } else {
 487                     /*
 488                      * According to XDnD protocol, XdndActionList is optional.
 489                      * If XdndActionList is not set we try to guess which actions are
 490                      * supported.
 491                      */
 492                     actions = DnDConstants.ACTION_COPY;
 493                     track = true;
 494                 }
 495             } finally {
 496                 wpg.dispose();
 497             }
 498         }
 499 
 500         /* Extract the available data types. */
 501         if ((xclient.get_data(1) & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) {
 502             WindowPropertyGetter wpg =
 503                 new WindowPropertyGetter(source_win,
 504                                          XDnDConstants.XA_XdndTypeList,
 505                                          0, 0xFFFF, false,
 506                                          XAtom.XA_ATOM);
 507             try {
 508                 wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 509 
 510                 if (wpg.getActualType() == XAtom.XA_ATOM &&
 511                     wpg.getActualFormat() == 32) {
 512                     formats = Native.toLongs(wpg.getData(),
 513                                              wpg.getNumberOfItems());
 514                 } else {
 515                     formats = new long[0];
 516                 }
 517             } finally {
 518                 wpg.dispose();
 519             }
 520         } else {
 521             int countFormats = 0;
 522             long[] formats3 = new long[3];
 523 
 524             for (int i = 0; i < 3; i++) {
 525                 long j;
 526                 if ((j = xclient.get_data(2 + i)) != XConstants.None) {
 527                     formats3[countFormats++] = j;
 528                 }
 529             }
 530 
 531             formats = new long[countFormats];
 532 
 533             System.arraycopy(formats3, 0, formats, 0, countFormats);
 534         }
 535 
 536         assert XToolkit.isAWTLockHeldByCurrentThread();
 537 
 538         /*
 539          * Select for StructureNotifyMask to receive DestroyNotify in case of source
 540          * crash.
 541          */
 542         XWindowAttributes wattr = new XWindowAttributes();
 543         try {
 544             XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 545             int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 546                                                           source_win, wattr.pData);
 547 
 548             XToolkit.RESTORE_XERROR_HANDLER();
 549 
 550             if (status == 0 ||
 551                 (XToolkit.saved_error != null &&
 552                  XToolkit.saved_error.get_error_code() != XConstants.Success)) {
 553                 throw new XException("XGetWindowAttributes failed");
 554             }
 555 
 556             source_win_mask = wattr.get_your_event_mask();
 557         } finally {
 558             wattr.dispose();
 559         }
 560 
 561         XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 562         XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win,
 563                                  source_win_mask |
 564                                  XConstants.StructureNotifyMask);
 565 
 566         XToolkit.RESTORE_XERROR_HANDLER();
 567 
 568         if (XToolkit.saved_error != null &&
 569             XToolkit.saved_error.get_error_code() != XConstants.Success) {
 570             throw new XException("XSelectInput failed");
 571         }
 572 
 573         sourceWindow = source_win;
 574         sourceWindowMask = source_win_mask;
 575         sourceProtocolVersion = protocol_version;
 576         sourceActions = actions;
 577         sourceFormats = formats;
 578         trackSourceActions = track;
 579 
 580         return true;
 581     }
 582 
 583     private boolean processXdndPosition(XClientMessageEvent xclient) {
 584         long time_stamp = (int)XConstants.CurrentTime;
 585         long xdnd_action = 0;
 586         int java_action = DnDConstants.ACTION_NONE;
 587         int x = 0;
 588         int y = 0;
 589 
 590         /* Ignore XDnD messages from all other windows. */
 591         if (sourceWindow != xclient.get_data(0)) {
 592             return false;
 593         }
 594 
 595         XWindow xwindow = null;
 596         {
 597             XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
 598             if (xbasewindow instanceof XWindow) {
 599                 xwindow = (XWindow)xbasewindow;
 600             }
 601         }
 602 
 603         x = (int)(xclient.get_data(2) >> 16);
 604         y = (int)(xclient.get_data(2) & 0xFFFF);
 605 
 606         if (xwindow == null) {
 607             long receiver =
 608                 XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
 609                     xclient.get_window(), x, y);
 610 
 611             if (receiver != 0) {
 612                 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
 613                 if (xbasewindow instanceof XWindow) {
 614                     xwindow = (XWindow)xbasewindow;
 615                 }
 616             }
 617         }
 618 
 619         if (xwindow != null) {
 620             /* Translate mouse position from root coordinates
 621                to the target window coordinates. */
 622             Point p = xwindow.toLocal(x, y);
 623             x = p.x;
 624             y = p.y;
 625         }
 626 
 627         /* Time stamp - new in XDnD version 1. */
 628         if (sourceProtocolVersion > 0) {
 629             time_stamp = xclient.get_data(3);
 630         }
 631 
 632         /* User action - new in XDnD version 2. */
 633         if (sourceProtocolVersion > 1) {
 634             xdnd_action = xclient.get_data(4);
 635         } else {
 636             /* The default action is XdndActionCopy */
 637             xdnd_action = XDnDConstants.XA_XdndActionCopy.getAtom();
 638         }
 639 
 640         java_action = XDnDConstants.getJavaActionForXDnDAction(xdnd_action);
 641 
 642         if (trackSourceActions) {
 643             sourceActions |= java_action;
 644         }
 645 
 646         if (xwindow == null) {
 647             if (targetXWindow != null) {
 648                 notifyProtocolListener(targetXWindow, x, y,
 649                                        DnDConstants.ACTION_NONE, xclient,
 650                                        MouseEvent.MOUSE_EXITED);
 651             }
 652         } else {
 653             int java_event_id = 0;
 654 
 655             if (targetXWindow == null) {
 656                 java_event_id = MouseEvent.MOUSE_ENTERED;
 657             } else {
 658                 java_event_id = MouseEvent.MOUSE_DRAGGED;
 659             }
 660 
 661             notifyProtocolListener(xwindow, x, y, java_action, xclient,
 662                                    java_event_id);
 663         }
 664 
 665         userAction = java_action;
 666         sourceX = x;
 667         sourceY = y;
 668         targetXWindow = xwindow;
 669 
 670         return true;
 671     }
 672 
 673     private boolean processXdndLeave(XClientMessageEvent xclient) {
 674         /* Ignore XDnD messages from all other windows. */
 675         if (sourceWindow != xclient.get_data(0)) {
 676             return false;
 677         }
 678 
 679         cleanup();
 680 
 681         return true;
 682     }
 683 
 684     private boolean processXdndDrop(XClientMessageEvent xclient) {
 685         /* Ignore XDnD messages from all other windows. */
 686         if (sourceWindow != xclient.get_data(0)) {
 687             return false;
 688         }
 689 
 690         if (targetXWindow != null) {
 691             notifyProtocolListener(targetXWindow, sourceX, sourceY, userAction,
 692                                    xclient, MouseEvent.MOUSE_RELEASED);
 693         }
 694 
 695         return true;
 696     }
 697 
 698     public int getMessageType(XClientMessageEvent xclient) {
 699         long messageType = xclient.get_message_type();
 700 
 701         if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) {
 702             return ENTER_MESSAGE;
 703         } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) {
 704             return MOTION_MESSAGE;
 705         } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) {
 706             return LEAVE_MESSAGE;
 707         } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) {
 708             return DROP_MESSAGE;
 709         } else {
 710             return UNKNOWN_MESSAGE;
 711         }
 712     }
 713 
 714     protected boolean processClientMessageImpl(XClientMessageEvent xclient) {
 715         long messageType = xclient.get_message_type();
 716 
 717         if (messageType == XDnDConstants.XA_XdndEnter.getAtom()) {
 718             return processXdndEnter(xclient);
 719         } else if (messageType == XDnDConstants.XA_XdndPosition.getAtom()) {
 720             return processXdndPosition(xclient);
 721         } else if (messageType == XDnDConstants.XA_XdndLeave.getAtom()) {
 722             return processXdndLeave(xclient);
 723         } else if (messageType == XDnDConstants.XA_XdndDrop.getAtom()) {
 724             return processXdndDrop(xclient);
 725         } else {
 726             return false;
 727         }
 728     }
 729 
 730     protected void sendEnterMessageToToplevel(long toplevel,
 731                                               XClientMessageEvent xclient) {
 732         /* flags */
 733         long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT;
 734         if (sourceFormats != null && sourceFormats.length > 3) {
 735             data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
 736         }
 737         long data2 = sourceFormats.length > 0 ? sourceFormats[0] : 0;
 738         long data3 = sourceFormats.length > 1 ? sourceFormats[1] : 0;
 739         long data4 = sourceFormats.length > 2 ? sourceFormats[2] : 0;
 740         sendEnterMessageToToplevelImpl(toplevel, xclient.get_data(0),
 741                                        data1, data2, data3, data4);
 742 
 743     }
 744 
 745     private void sendEnterMessageToToplevelImpl(long toplevel,
 746                                                 long sourceWindow,
 747                                                 long data1, long data2,
 748                                                 long data3, long data4) {
 749         XClientMessageEvent enter = new XClientMessageEvent();
 750         try {
 751             enter.set_type((int)XConstants.ClientMessage);
 752             enter.set_window(toplevel);
 753             enter.set_format(32);
 754             enter.set_message_type(XDnDConstants.XA_XdndEnter.getAtom());
 755             /* XID of the source window */
 756             enter.set_data(0, sourceWindow);
 757             enter.set_data(1, data1);
 758             enter.set_data(2, data2);
 759             enter.set_data(3, data3);
 760             enter.set_data(4, data4);
 761 
 762             forwardClientMessageToToplevel(toplevel, enter);
 763         } finally {
 764             enter.dispose();
 765         }
 766     }
 767 
 768     protected void sendLeaveMessageToToplevel(long toplevel,
 769                                               XClientMessageEvent xclient) {
 770         sendLeaveMessageToToplevelImpl(toplevel, xclient.get_data(0));
 771     }
 772 
 773     protected void sendLeaveMessageToToplevelImpl(long toplevel,
 774                                                   long sourceWindow) {
 775         XClientMessageEvent leave = new XClientMessageEvent();
 776         try {
 777             leave.set_type((int)XConstants.ClientMessage);
 778             leave.set_window(toplevel);
 779             leave.set_format(32);
 780             leave.set_message_type(XDnDConstants.XA_XdndLeave.getAtom());
 781             /* XID of the source window */
 782             leave.set_data(0, sourceWindow);
 783             /* flags */
 784             leave.set_data(1, 0);
 785 
 786             forwardClientMessageToToplevel(toplevel, leave);
 787         } finally {
 788             leave.dispose();
 789         }
 790     }
 791 
 792     public boolean sendResponse(long ctxt, int eventID, int action) {
 793         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
 794 
 795         if (xclient.get_message_type() !=
 796             XDnDConstants.XA_XdndPosition.getAtom()) {
 797 
 798             return false;
 799         }
 800 
 801         if (eventID == MouseEvent.MOUSE_EXITED) {
 802             action = DnDConstants.ACTION_NONE;
 803         }
 804 
 805         XClientMessageEvent msg = new XClientMessageEvent();
 806         try {
 807             msg.set_type((int)XConstants.ClientMessage);
 808             msg.set_window(xclient.get_data(0));
 809             msg.set_format(32);
 810             msg.set_message_type(XDnDConstants.XA_XdndStatus.getAtom());
 811             /* target window */
 812             msg.set_data(0, xclient.get_window());
 813             /* flags */
 814             long flags = 0;
 815             if (action != DnDConstants.ACTION_NONE) {
 816                 flags |= XDnDConstants.XDND_ACCEPT_DROP_FLAG;
 817             }
 818             msg.set_data(1, flags);
 819             /* specify an empty rectangle */
 820             msg.set_data(2, 0); /* x, y */
 821             msg.set_data(3, 0); /* w, h */
 822             /* action accepted by the target */
 823             msg.set_data(4, XDnDConstants.getXDnDActionForJavaAction(action));
 824 
 825             XToolkit.awtLock();
 826             try {
 827                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 828                                        xclient.get_data(0),
 829                                        false, XConstants.NoEventMask,
 830                                        msg.pData);
 831             } finally {
 832                 XToolkit.awtUnlock();
 833             }
 834         } finally {
 835             msg.dispose();
 836         }
 837 
 838         return true;
 839     }
 840 
 841     public Object getData(long ctxt, long format)
 842       throws IllegalArgumentException, IOException {
 843         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
 844         long message_type = xclient.get_message_type();
 845         long time_stamp = XConstants.CurrentTime;
 846 
 847         // NOTE: we assume that the source supports at least version 1, so we
 848         // can use the time stamp
 849         if (message_type == XDnDConstants.XA_XdndPosition.getAtom()) {
 850             // X server time is an unsigned 32-bit number!
 851             time_stamp = xclient.get_data(3) & 0xFFFFFFFFL;
 852         } else if (message_type == XDnDConstants.XA_XdndDrop.getAtom()) {
 853             // X server time is an unsigned 32-bit number!
 854             time_stamp = xclient.get_data(2) & 0xFFFFFFFFL;
 855         } else {
 856             throw new IllegalArgumentException();
 857         }
 858 
 859         return XDnDConstants.XDnDSelection.getData(format, time_stamp);
 860     }
 861 
 862     public boolean sendDropDone(long ctxt, boolean success, int dropAction) {
 863         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
 864 
 865         if (xclient.get_message_type() !=
 866             XDnDConstants.XA_XdndDrop.getAtom()) {
 867             return false;
 868         }
 869 
 870         /*
 871          * The XDnD protocol recommends that the target requests the special
 872          * target DELETE in case if the drop action is XdndActionMove.
 873          */
 874         if (dropAction == DnDConstants.ACTION_MOVE && success) {
 875 
 876             long time_stamp = xclient.get_data(2);
 877             long xdndSelectionAtom =
 878                 XDnDConstants.XDnDSelection.getSelectionAtom().getAtom();
 879 
 880             XToolkit.awtLock();
 881             try {
 882                 XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
 883                                               xdndSelectionAtom,
 884                                               XAtom.get("DELETE").getAtom(),
 885                                               XAtom.get("XAWT_SELECTION").getAtom(),
 886                                               XWindow.getXAWTRootWindow().getWindow(),
 887                                               time_stamp);
 888             } finally {
 889                 XToolkit.awtUnlock();
 890             }
 891         }
 892 
 893         XClientMessageEvent msg = new XClientMessageEvent();
 894         try {
 895             msg.set_type((int)XConstants.ClientMessage);
 896             msg.set_window(xclient.get_data(0));
 897             msg.set_format(32);
 898             msg.set_message_type(XDnDConstants.XA_XdndFinished.getAtom());
 899             msg.set_data(0, xclient.get_window()); /* target window */
 900             msg.set_data(1, 0); /* flags */
 901             /* specify an empty rectangle */
 902             msg.set_data(2, 0);
 903             if (sourceProtocolVersion >= 5) {
 904                 if (success) {
 905                     msg.set_data(1, XDnDConstants.XDND_ACCEPT_DROP_FLAG);
 906                 }
 907                 /* action performed by the target */
 908                 msg.set_data(2, XDnDConstants.getXDnDActionForJavaAction(dropAction));
 909             }
 910             msg.set_data(3, 0);
 911             msg.set_data(4, 0);
 912 
 913             XToolkit.awtLock();
 914             try {
 915                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 916                                        xclient.get_data(0),
 917                                        false, XConstants.NoEventMask,
 918                                        msg.pData);
 919             } finally {
 920                 XToolkit.awtUnlock();
 921             }
 922         } finally {
 923             msg.dispose();
 924         }
 925 
 926         /*
 927          * Flush the buffer to guarantee that the drop completion event is sent
 928          * to the source before the method returns.
 929          */
 930         XToolkit.awtLock();
 931         try {
 932             XlibWrapper.XFlush(XToolkit.getDisplay());
 933         } finally {
 934             XToolkit.awtUnlock();
 935         }
 936 
 937         /* Trick to prevent cleanup() from posting dragExit */
 938         targetXWindow = null;
 939 
 940         /* Cannot do cleanup before the drop finishes as we may need
 941            source protocol version to send drop finished message. */
 942         cleanup();
 943         return true;
 944     }
 945 
 946     public final long getSourceWindow() {
 947         return sourceWindow;
 948     }
 949 
 950     /**
 951      * Reset the state of the object.
 952      */
 953     public void cleanup() {
 954         // Clear the reference to this protocol.
 955         XDropTargetEventProcessor.reset();
 956 
 957         if (targetXWindow != null) {
 958             notifyProtocolListener(targetXWindow, 0, 0,
 959                                    DnDConstants.ACTION_NONE, null,
 960                                    MouseEvent.MOUSE_EXITED);
 961         }
 962 
 963         if (sourceWindow != 0) {
 964             XToolkit.awtLock();
 965             try {
 966                 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 967                 XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow,
 968                                          sourceWindowMask);
 969                 XToolkit.RESTORE_XERROR_HANDLER();
 970             } finally {
 971                 XToolkit.awtUnlock();
 972             }
 973         }
 974 
 975         sourceWindow = 0;
 976         sourceWindowMask = 0;
 977         sourceProtocolVersion = 0;
 978         sourceActions = DnDConstants.ACTION_NONE;
 979         sourceFormats = null;
 980         trackSourceActions = false;
 981         userAction = DnDConstants.ACTION_NONE;
 982         sourceX = 0;
 983         sourceY = 0;
 984         targetXWindow = null;
 985     }
 986 
 987     public boolean isDragOverComponent() {
 988         return targetXWindow != null;
 989     }
 990 
 991     public void adjustEventForForwarding(XClientMessageEvent xclient,
 992                                          EmbedderRegistryEntry entry) {
 993         /* Adjust the event to match the XDnD protocol version. */
 994         int version = entry.getVersion();
 995         if (xclient.get_message_type() == XDnDConstants.XA_XdndEnter.getAtom()) {
 996             int min_version = sourceProtocolVersion < version ?
 997                 sourceProtocolVersion : version;
 998             long data1 = min_version << XDnDConstants.XDND_PROTOCOL_SHIFT;
 999             if (sourceFormats != null && sourceFormats.length > 3) {
1000                 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
1001             }
1002             if (logger.isLoggable(PlatformLogger.FINEST)) {
1003                 logger.finest("         "
1004                               + " entryVersion=" + version
1005                               + " sourceProtocolVersion=" +
1006                               sourceProtocolVersion
1007                               + " sourceFormats.length=" +
1008                               (sourceFormats != null ? sourceFormats.length : 0));
1009             }
1010             xclient.set_data(1, data1);
1011         }
1012     }
1013 
1014     private void notifyProtocolListener(XWindow xwindow, int x, int y,
1015                                         int dropAction,
1016                                         XClientMessageEvent xclient,
1017                                         int eventID) {
1018         long nativeCtxt = 0;
1019 
1020         // Make a copy of the passed XClientMessageEvent structure, since
1021         // the original structure can be freed before this
1022         // SunDropTargetEvent is dispatched.
1023         if (xclient != null) {
1024             int size = new XClientMessageEvent(nativeCtxt).getSize();
1025 
1026             nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1027 
1028             unsafe.copyMemory(xclient.pData, nativeCtxt, size);
1029 
1030             long data1 = sourceProtocolVersion << XDnDConstants.XDND_PROTOCOL_SHIFT;
1031             if (sourceFormats != null && sourceFormats.length > 3) {
1032                 data1 |= XDnDConstants.XDND_DATA_TYPES_BIT;
1033             }
1034             // Append information from the latest XdndEnter event.
1035             Native.putLong(nativeCtxt + size, data1);
1036             Native.putLong(nativeCtxt + size + Native.getLongSize(),
1037                            sourceFormats.length > 0 ? sourceFormats[0] : 0);
1038             Native.putLong(nativeCtxt + size + 2 * Native.getLongSize(),
1039                            sourceFormats.length > 1 ? sourceFormats[1] : 0);
1040             Native.putLong(nativeCtxt + size + 3 * Native.getLongSize(),
1041                            sourceFormats.length > 2 ? sourceFormats[2] : 0);
1042         }
1043 
1044         getProtocolListener().handleDropTargetNotification(xwindow, x, y,
1045                                                            dropAction,
1046                                                            sourceActions,
1047                                                            sourceFormats,
1048                                                            nativeCtxt,
1049                                                            eventID);
1050     }
1051 
1052     /*
1053      * The methods/fields defined below are executed/accessed only on
1054      * the toolkit thread.
1055      * The methods/fields defined below are executed/accessed only on the event
1056      * dispatch thread.
1057      */
1058 
1059     public boolean forwardEventToEmbedded(long embedded, long ctxt,
1060                                           int eventID) {
1061         if (logger.isLoggable(PlatformLogger.FINEST)) {
1062             logger.finest("        ctxt=" + ctxt +
1063                           " type=" + (ctxt != 0 ?
1064                                       getMessageType(new
1065                                           XClientMessageEvent(ctxt)) : 0) +
1066                           " prevCtxt=" + prevCtxt +
1067                           " prevType=" + (prevCtxt != 0 ?
1068                                       getMessageType(new
1069                                           XClientMessageEvent(prevCtxt)) : 0));
1070         }
1071         if ((ctxt == 0 ||
1072              getMessageType(new XClientMessageEvent(ctxt)) == UNKNOWN_MESSAGE) &&
1073             (prevCtxt == 0 ||
1074              getMessageType(new XClientMessageEvent(prevCtxt)) == UNKNOWN_MESSAGE)) {
1075             return false;
1076         }
1077 
1078         // The size of XClientMessageEvent structure.
1079         int size = XClientMessageEvent.getSize();
1080 
1081         if (ctxt != 0) {
1082             XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
1083             if (!overXEmbedClient) {
1084                 long data1 = Native.getLong(ctxt + size);
1085                 long data2 = Native.getLong(ctxt + size + Native.getLongSize());
1086                 long data3 = Native.getLong(ctxt + size + 2 * Native.getLongSize());
1087                 long data4 = Native.getLong(ctxt + size + 3 * Native.getLongSize());
1088 
1089                 if (logger.isLoggable(PlatformLogger.FINEST)) {
1090                     logger.finest("         1 "
1091                                   + " embedded=" + embedded
1092                                   + " source=" + xclient.get_data(0)
1093                                   + " data1=" + data1
1094                                   + " data2=" + data2
1095                                   + " data3=" + data3
1096                                   + " data4=" + data4);
1097                 }
1098 
1099                 // Copy XdndTypeList from source to proxy.
1100                 if ((data1 & XDnDConstants.XDND_DATA_TYPES_BIT) != 0) {
1101                     WindowPropertyGetter wpg =
1102                         new WindowPropertyGetter(xclient.get_data(0),
1103                                                  XDnDConstants.XA_XdndTypeList,
1104                                                  0, 0xFFFF, false,
1105                                                  XAtom.XA_ATOM);
1106                     try {
1107                         wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
1108 
1109                         if (wpg.getActualType() == XAtom.XA_ATOM &&
1110                             wpg.getActualFormat() == 32) {
1111 
1112                             XToolkit.awtLock();
1113                             try {
1114                                 XToolkit.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
1115                                 XDnDConstants.XA_XdndTypeList.setAtomData(xclient.get_window(),
1116                                                                           XAtom.XA_ATOM,
1117                                                                           wpg.getData(),
1118                                                                           wpg.getNumberOfItems());
1119                                 XToolkit.RESTORE_XERROR_HANDLER();
1120 
1121                                 if (XToolkit.saved_error != null &&
1122                                     XToolkit.saved_error.get_error_code() != XConstants.Success) {
1123                                     if (logger.isLoggable(PlatformLogger.WARNING)) {
1124                                         logger.warning("Cannot set XdndTypeList on the proxy window");
1125                                     }
1126                                 }
1127                             } finally {
1128                                 XToolkit.awtUnlock();
1129                             }
1130                         } else {
1131                             if (logger.isLoggable(PlatformLogger.WARNING)) {
1132                                 logger.warning("Cannot read XdndTypeList from the source window");
1133                             }
1134                         }
1135                     } finally {
1136                         wpg.dispose();
1137                     }
1138                 }
1139                 XDragSourceContextPeer.setProxyModeSourceWindow(xclient.get_data(0));
1140 
1141                 sendEnterMessageToToplevelImpl(embedded, xclient.get_window(),
1142                                                data1, data2, data3, data4);
1143                 overXEmbedClient = true;
1144             }
1145 
1146             if (logger.isLoggable(PlatformLogger.FINEST)) {
1147                 logger.finest("         2 "
1148                               + " embedded=" + embedded
1149                               + " xclient=" + xclient);
1150             }
1151 
1152             /* Make a copy of the original event, since we are going to modify the
1153                event while it still can be referenced from other Java events. */
1154             {
1155                 XClientMessageEvent copy = new XClientMessageEvent();
1156                 unsafe.copyMemory(xclient.pData, copy.pData, copy.getSize());
1157 
1158                 copy.set_data(0, xclient.get_window());
1159 
1160                 forwardClientMessageToToplevel(embedded, copy);
1161             }
1162         }
1163 
1164         if (eventID == MouseEvent.MOUSE_EXITED) {
1165             if (overXEmbedClient) {
1166                 if (ctxt != 0 || prevCtxt != 0) {
1167                     // Last chance to send XdndLeave to the XEmbed client.
1168                     XClientMessageEvent xclient = ctxt != 0 ?
1169                         new XClientMessageEvent(ctxt) :
1170                         new XClientMessageEvent(prevCtxt);
1171                     sendLeaveMessageToToplevelImpl(embedded, xclient.get_window());
1172                 }
1173                 overXEmbedClient = false;
1174                 // We have to clear the proxy mode source window here,
1175                 // when the drag exits the XEmbedCanvasPeer.
1176                 // NOTE: at this point the XEmbed client still might have some
1177                 // drag notifications to process and it will send responses to
1178                 // us. With the proxy mode source window cleared we won't be
1179                 // able to forward these responses to the actual source. This is
1180                 // not a problem if the drag operation was initiated in this
1181                 // JVM. However, if it was initiated in another processes the
1182                 // responses will be lost. We bear with it for now, as it seems
1183                 // there is no other reliable point to clear.
1184                 XDragSourceContextPeer.setProxyModeSourceWindow(0);
1185             }
1186         }
1187 
1188         if (eventID == MouseEvent.MOUSE_RELEASED) {
1189             overXEmbedClient = false;
1190             cleanup();
1191         }
1192 
1193         if (prevCtxt != 0) {
1194             unsafe.freeMemory(prevCtxt);
1195             prevCtxt = 0;
1196         }
1197 
1198         if (ctxt != 0 && overXEmbedClient) {
1199             prevCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1200 
1201             unsafe.copyMemory(ctxt, prevCtxt, size + 4 * Native.getLongSize());
1202         }
1203 
1204         return true;
1205     }
1206 
1207     public boolean isXEmbedSupported() {
1208         return true;
1209     }
1210 }