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