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.misc.Unsafe;
  37 
  38 /**
  39  * XDropTargetProtocol implementation for Motif DnD protocol.
  40  *
  41  * @since 1.5
  42  */
  43 class MotifDnDDropTargetProtocol extends XDropTargetProtocol {
  44     private static final Unsafe unsafe = XlibWrapper.unsafe;
  45 
  46     private long sourceWindow = 0;
  47     private long sourceWindowMask = 0;
  48     private int sourceProtocolVersion = 0;
  49     private int sourceActions = DnDConstants.ACTION_NONE;
  50     private long[] sourceFormats = null;
  51     private long sourceAtom = 0;
  52     private int userAction = DnDConstants.ACTION_NONE;
  53     private int sourceX = 0;
  54     private int sourceY = 0;
  55     private XWindow targetXWindow = null;
  56     private boolean topLevelLeavePostponed = false;
  57 
  58     protected MotifDnDDropTargetProtocol(XDropTargetProtocolListener listener) {
  59         super(listener);
  60     }
  61 
  62     /**
  63      * Creates an instance associated with the specified listener.
  64      *
  65      * @throws NullPointerException if listener is <code>null</code>.
  66      */
  67     static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) {
  68         return new MotifDnDDropTargetProtocol(listener);
  69     }
  70 
  71     public String getProtocolName() {
  72         return XDragAndDropProtocols.MotifDnD;
  73     }
  74 
  75     public void registerDropTarget(long window) {
  76         assert XToolkit.isAWTLockHeldByCurrentThread();
  77 
  78         MotifDnDConstants.writeDragReceiverInfoStruct(window);
  79     }
  80 
  81     public void unregisterDropTarget(long window) {
  82         assert XToolkit.isAWTLockHeldByCurrentThread();
  83 
  84         MotifDnDConstants.XA_MOTIF_ATOM_0.DeleteProperty(window);
  85     }
  86 
  87     public void registerEmbedderDropSite(long embedder) {
  88         assert XToolkit.isAWTLockHeldByCurrentThread();
  89 
  90         boolean overriden = false;
  91         int version = 0;
  92         long proxy = 0;
  93         long newProxy = XDropTargetRegistry.getDnDProxyWindow();
  94         int status = 0;
  95         long data = 0;
  96         int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE;
  97 
  98         WindowPropertyGetter wpg =
  99             new WindowPropertyGetter(embedder,
 100                                      MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
 101                                      0, 0xFFFF, false,
 102                                      XConstants.AnyPropertyType);
 103 
 104         try {
 105             status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 106 
 107             /*
 108              * DragICCI.h:
 109              *
 110              * typedef struct _xmDragReceiverInfoStruct{
 111              *     BYTE byte_order;
 112              *     BYTE protocol_version;
 113              *     BYTE drag_protocol_style;
 114              *     BYTE pad1;
 115              *     CARD32       proxy_window B32;
 116              *     CARD16       num_drop_sites B16;
 117              *     CARD16       pad2 B16;
 118              *     CARD32       heap_offset B32;
 119              * } xmDragReceiverInfoStruct;
 120              */
 121             if (status == XConstants.Success && wpg.getData() != 0 &&
 122                 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
 123                 wpg.getNumberOfItems() >=
 124                 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
 125 
 126                 overriden = true;
 127                 data = wpg.getData();
 128                 dataSize = wpg.getNumberOfItems();
 129 
 130                 byte byteOrderByte = unsafe.getByte(data);
 131 
 132                 {
 133                     int tproxy = unsafe.getInt(data + 4);
 134                     if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
 135                         tproxy = MotifDnDConstants.Swapper.swap(tproxy);
 136                     }
 137                     proxy = tproxy;
 138                 }
 139 
 140                 if (proxy == newProxy) {
 141                     // Embedder already registered.
 142                     return;
 143                 }
 144 
 145                 {
 146                     int tproxy = (int)newProxy;
 147                     if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
 148                         tproxy = MotifDnDConstants.Swapper.swap(tproxy);
 149                     }
 150                     unsafe.putInt(data + 4, tproxy);
 151                 }
 152             } else {
 153                 data = unsafe.allocateMemory(dataSize);
 154 
 155                 unsafe.putByte(data, MotifDnDConstants.getByteOrderByte()); /* byte order */
 156                 unsafe.putByte(data + 1, MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION); /* protocol version */
 157                 unsafe.putByte(data + 2, (byte)MotifDnDConstants.MOTIF_DYNAMIC_STYLE); /* protocol style */
 158                 unsafe.putByte(data + 3, (byte)0); /* pad */
 159                 unsafe.putInt(data + 4, (int)newProxy); /* proxy window */
 160                 unsafe.putShort(data + 8, (short)0); /* num_drop_sites */
 161                 unsafe.putShort(data + 10, (short)0); /* pad */
 162                 unsafe.putInt(data + 12, dataSize);
 163             }
 164 
 165             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 166             XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder,
 167                                         MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
 168                                         MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
 169                                         8, XConstants.PropModeReplace,
 170                                         data, dataSize);
 171             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 172 
 173             if ((XErrorHandlerUtil.saved_error != null) &&
 174                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 175                 throw new XException("Cannot write Motif receiver info property");
 176             }
 177         } finally {
 178             if (!overriden) {
 179                 unsafe.freeMemory(data);
 180                 data = 0;
 181             }
 182             wpg.dispose();
 183         }
 184 
 185         putEmbedderRegistryEntry(embedder, overriden, version, proxy);
 186     }
 187 
 188     public void unregisterEmbedderDropSite(long embedder) {
 189         assert XToolkit.isAWTLockHeldByCurrentThread();
 190 
 191         EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder);
 192 
 193         if (entry == null) {
 194             return;
 195         }
 196 
 197         if (entry.isOverriden()) {
 198             int status = 0;
 199 
 200             WindowPropertyGetter wpg =
 201                 new WindowPropertyGetter(embedder,
 202                                          MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
 203                                          0, 0xFFFF, false,
 204                                          XConstants.AnyPropertyType);
 205 
 206             try {
 207                 status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 208 
 209                 /*
 210                  * DragICCI.h:
 211                  *
 212                  * typedef struct _xmDragReceiverInfoStruct{
 213                  *     BYTE     byte_order;
 214                  *     BYTE     protocol_version;
 215                  *     BYTE     drag_protocol_style;
 216                  *     BYTE     pad1;
 217                  *     CARD32   proxy_window B32;
 218                  *     CARD16   num_drop_sites B16;
 219                  *     CARD16   pad2 B16;
 220                  *     CARD32   heap_offset B32;
 221                  * } xmDragReceiverInfoStruct;
 222                  */
 223                 if (status == XConstants.Success && wpg.getData() != 0 &&
 224                     wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
 225                     wpg.getNumberOfItems() >=
 226                     MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
 227 
 228                     int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE;
 229                     long data = wpg.getData();
 230                     byte byteOrderByte = unsafe.getByte(data);
 231 
 232                     int tproxy = (int)entry.getProxy();
 233                     if (MotifDnDConstants.getByteOrderByte() != byteOrderByte) {
 234                         tproxy = MotifDnDConstants.Swapper.swap(tproxy);
 235                     }
 236 
 237                     unsafe.putInt(data + 4, tproxy);
 238 
 239                     XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
 240                     XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder,
 241                                                 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
 242                                                 MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
 243                                                 8, XConstants.PropModeReplace,
 244                                                 data, dataSize);
 245                     XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 246 
 247                     if ((XErrorHandlerUtil.saved_error != null) &&
 248                         (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 249                         throw new XException("Cannot write Motif receiver info property");
 250                     }
 251                 }
 252             } finally {
 253                 wpg.dispose();
 254             }
 255         } else {
 256             MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.DeleteProperty(embedder);
 257         }
 258     }
 259 
 260     /*
 261      * Gets and stores in the registry the embedder's Motif DnD drop site info
 262      * from the embedded.
 263      */
 264     public void registerEmbeddedDropSite(long embedded) {
 265         assert XToolkit.isAWTLockHeldByCurrentThread();
 266 
 267         boolean overriden = false;
 268         int version = 0;
 269         long proxy = 0;
 270         int status = 0;
 271 
 272         WindowPropertyGetter wpg =
 273             new WindowPropertyGetter(embedded,
 274                                      MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
 275                                      0, 0xFFFF, false,
 276                                      XConstants.AnyPropertyType);
 277 
 278         try {
 279             status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 280 
 281             /*
 282              * DragICCI.h:
 283              *
 284              * typedef struct _xmDragReceiverInfoStruct{
 285              *     BYTE byte_order;
 286              *     BYTE protocol_version;
 287              *     BYTE drag_protocol_style;
 288              *     BYTE pad1;
 289              *     CARD32       proxy_window B32;
 290              *     CARD16       num_drop_sites B16;
 291              *     CARD16       pad2 B16;
 292              *     CARD32       heap_offset B32;
 293              * } xmDragReceiverInfoStruct;
 294              */
 295             if (status == XConstants.Success && wpg.getData() != 0 &&
 296                 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
 297                 wpg.getNumberOfItems() >=
 298                 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
 299 
 300                 overriden = true;
 301                 long data = wpg.getData();
 302 
 303                 byte byteOrderByte = unsafe.getByte(data);
 304 
 305                 {
 306                     int tproxy = unsafe.getInt(data + 4);
 307                     if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
 308                         tproxy = MotifDnDConstants.Swapper.swap(tproxy);
 309                     }
 310                     proxy = tproxy;
 311                 }
 312             }
 313         } finally {
 314             wpg.dispose();
 315         }
 316 
 317         putEmbedderRegistryEntry(embedded, overriden, version, proxy);
 318     }
 319 
 320     public boolean isProtocolSupported(long window) {
 321         WindowPropertyGetter wpg =
 322             new WindowPropertyGetter(window,
 323                                      MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
 324                                      0, 0xFFFF, false,
 325                                      XConstants.AnyPropertyType);
 326 
 327         try {
 328             int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 329 
 330             if (status == XConstants.Success && wpg.getData() != 0 &&
 331                 wpg.getActualType() != 0 && wpg.getActualFormat() == 8 &&
 332                 wpg.getNumberOfItems() >=
 333                 MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) {
 334                 return true;
 335             } else {
 336                 return false;
 337             }
 338         } finally {
 339             wpg.dispose();
 340         }
 341     }
 342 
 343     private boolean processTopLevelEnter(XClientMessageEvent xclient) {
 344         assert XToolkit.isAWTLockHeldByCurrentThread();
 345 
 346         if (targetXWindow != null || sourceWindow != 0) {
 347             return false;
 348         }
 349 
 350         if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow)
 351             && getEmbedderRegistryEntry(xclient.get_window()) == null) {
 352             return false;
 353         }
 354 
 355         long source_win = 0;
 356         long source_win_mask = 0;
 357         int protocol_version = 0;
 358         long property_atom = 0;
 359         long[] formats = null;
 360 
 361         {
 362             long data = xclient.get_data();
 363             byte eventByteOrder = unsafe.getByte(data + 1);
 364             source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder);
 365             property_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
 366         }
 367 
 368         /* Extract the available data types. */
 369         {
 370             WindowPropertyGetter wpg =
 371                 new WindowPropertyGetter(source_win,
 372                                          XAtom.get(property_atom),
 373                                          0, 0xFFFF,
 374                                          false,
 375                                          MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom());
 376 
 377             try {
 378                 int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 379 
 380                 if (status == XConstants.Success && wpg.getData() != 0 &&
 381                     wpg.getActualType() ==
 382                     MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom() &&
 383                     wpg.getActualFormat() == 8 &&
 384                     wpg.getNumberOfItems() ==
 385                     MotifDnDConstants.MOTIF_INITIATOR_INFO_SIZE) {
 386 
 387                     long data = wpg.getData();
 388                     byte propertyByteOrder = unsafe.getByte(data);
 389 
 390                     protocol_version = unsafe.getByte(data + 1);
 391 
 392                     if (protocol_version !=
 393                         MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION) {
 394                         return false;
 395                     }
 396 
 397                     int index =
 398                         MotifDnDConstants.Swapper.getShort(data + 2, propertyByteOrder);
 399 
 400                     formats = MotifDnDConstants.getTargetListForIndex(index);
 401                 } else {
 402                     formats = new long[0];
 403                 }
 404             } finally {
 405                 wpg.dispose();
 406             }
 407         }
 408 
 409         /*
 410          * Select for StructureNotifyMask to receive DestroyNotify in case of source
 411          * crash.
 412          */
 413         XWindowAttributes wattr = new XWindowAttributes();
 414         try {
 415             XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 416             int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 417                                                           source_win, wattr.pData);
 418 
 419             XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 420 
 421             if ((status == 0) ||
 422                 ((XErrorHandlerUtil.saved_error != null) &&
 423                 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
 424                 throw new XException("XGetWindowAttributes failed");
 425             }
 426 
 427             source_win_mask = wattr.get_your_event_mask();
 428         } finally {
 429             wattr.dispose();
 430         }
 431 
 432         XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 433         XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win,
 434                                  source_win_mask |
 435                                  XConstants.StructureNotifyMask);
 436 
 437         XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 438 
 439         if ((XErrorHandlerUtil.saved_error != null) &&
 440             (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 441             throw new XException("XSelectInput failed");
 442         }
 443 
 444         sourceWindow = source_win;
 445         sourceWindowMask = source_win_mask;
 446         sourceProtocolVersion = protocol_version;
 447         /*
 448          * TOP_LEVEL_ENTER doesn't communicate the list of supported actions
 449          * They are provided in DRAG_MOTION.
 450          */
 451         sourceActions = DnDConstants.ACTION_NONE;
 452         sourceFormats = formats;
 453         sourceAtom = property_atom;
 454 
 455         return true;
 456     }
 457 
 458     private boolean processDragMotion(XClientMessageEvent xclient) {
 459         long data = xclient.get_data();
 460         byte eventByteOrder = unsafe.getByte(data + 1);
 461         byte eventReason = (byte)(unsafe.getByte(data) &
 462                                   MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
 463         int x = 0;
 464         int y = 0;
 465 
 466         short flags = MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder);
 467 
 468         int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >>
 469             MotifDnDConstants.MOTIF_DND_ACTION_SHIFT;
 470         int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >>
 471             MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT;
 472 
 473         int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action);
 474         int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions);
 475 
 476         /* Append source window id to the event data, so that we can send the
 477            response properly. */
 478         {
 479             int win = (int)sourceWindow;
 480             if (eventByteOrder != MotifDnDConstants.getByteOrderByte()) {
 481                 win = MotifDnDConstants.Swapper.swap(win);
 482             }
 483             unsafe.putInt(data + 12, win);
 484         }
 485 
 486         XWindow xwindow = null;
 487         {
 488             XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
 489             if (xbasewindow instanceof XWindow) {
 490                 xwindow = (XWindow)xbasewindow;
 491             }
 492         }
 493 
 494         if (eventReason == MotifDnDConstants.OPERATION_CHANGED) {
 495             /* OPERATION_CHANGED event doesn't provide coordinates, so we use
 496                previously stored position and component ref. */
 497             x = sourceX;
 498             y = sourceY;
 499 
 500             if (xwindow == null) {
 501                 xwindow = targetXWindow;
 502             }
 503         } else {
 504             x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder);
 505             y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder);
 506 
 507             if (xwindow == null) {
 508                 long receiver =
 509                     XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
 510                         xclient.get_window(), x, y);
 511 
 512                 if (receiver != 0) {
 513                     XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
 514                     if (xbasewindow instanceof XWindow) {
 515                         xwindow = (XWindow)xbasewindow;
 516                     }
 517                 }
 518             }
 519 
 520             if (xwindow != null) {
 521                 Point p = xwindow.toLocal(x, y);
 522                 x = p.x;
 523                 y = p.y;
 524             }
 525         }
 526 
 527         if (xwindow == null) {
 528             if (targetXWindow != null) {
 529                 notifyProtocolListener(targetXWindow, x, y,
 530                                        DnDConstants.ACTION_NONE, java_actions,
 531                                        xclient, MouseEvent.MOUSE_EXITED);
 532             }
 533         } else {
 534             int java_event_id = 0;
 535 
 536             if (targetXWindow == null) {
 537                 java_event_id = MouseEvent.MOUSE_ENTERED;
 538             } else {
 539                 java_event_id = MouseEvent.MOUSE_DRAGGED;
 540             }
 541 
 542             notifyProtocolListener(xwindow, x, y, java_action, java_actions,
 543                                    xclient, java_event_id);
 544         }
 545 
 546         sourceActions = java_actions;
 547         userAction = java_action;
 548         sourceX = x;
 549         sourceY = y;
 550         targetXWindow = xwindow;
 551 
 552         return true;
 553     }
 554 
 555     private boolean processTopLevelLeave(XClientMessageEvent xclient) {
 556         assert XToolkit.isAWTLockHeldByCurrentThread();
 557 
 558         long data = xclient.get_data();
 559         byte eventByteOrder = unsafe.getByte(data + 1);
 560 
 561         long source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder);
 562 
 563         /* Ignore Motif DnD messages from all other windows. */
 564         if (source_win != sourceWindow) {
 565             return false;
 566         }
 567 
 568         /*
 569          * Postpone upcall to java, so that we can abort it in case
 570          * if drop immediatelly follows (see BugTraq ID 4395290).
 571          * Send a dummy ClientMessage event to guarantee that a postponed java
 572          * upcall will be processed.
 573          */
 574         topLevelLeavePostponed = true;
 575         {
 576             long proxy;
 577 
 578             /*
 579              * If this is an embedded drop site, the event should go to the
 580              * awt_root_window as this is a proxy for all embedded drop sites.
 581              * Otherwise the event should go to the event->window, as we don't use
 582              * proxies for normal drop sites.
 583              */
 584             if (getEmbedderRegistryEntry(xclient.get_window()) != null) {
 585                 proxy = XDropTargetRegistry.getDnDProxyWindow();
 586             } else {
 587                 proxy = xclient.get_window();
 588             }
 589 
 590             XClientMessageEvent dummy = new XClientMessageEvent();
 591 
 592             try {
 593                 dummy.set_type(XConstants.ClientMessage);
 594                 dummy.set_window(xclient.get_window());
 595                 dummy.set_format(32);
 596                 dummy.set_message_type(0);
 597                 dummy.set_data(0, 0);
 598                 dummy.set_data(1, 0);
 599                 dummy.set_data(2, 0);
 600                 dummy.set_data(3, 0);
 601                 dummy.set_data(4, 0);
 602                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 603                                        proxy, false, XConstants.NoEventMask,
 604                                        dummy.pData);
 605             } finally {
 606                 dummy.dispose();
 607             }
 608         }
 609         return true;
 610     }
 611 
 612     private boolean processDropStart(XClientMessageEvent xclient) {
 613         long data = xclient.get_data();
 614         byte eventByteOrder = unsafe.getByte(data + 1);
 615 
 616         long source_win =
 617             MotifDnDConstants.Swapper.getInt(data + 16, eventByteOrder);
 618 
 619         /* Ignore Motif DnD messages from all other windows. */
 620         if (source_win != sourceWindow) {
 621             return false;
 622         }
 623 
 624         long property_atom =
 625             MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
 626 
 627         short flags =
 628             MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder);
 629 
 630         int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >>
 631             MotifDnDConstants.MOTIF_DND_ACTION_SHIFT;
 632         int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >>
 633             MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT;
 634 
 635         int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action);
 636         int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions);
 637 
 638         int x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder);
 639         int y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder);
 640 
 641         XWindow xwindow = null;
 642         {
 643             XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window());
 644             if (xbasewindow instanceof XWindow) {
 645                 xwindow = (XWindow)xbasewindow;
 646             }
 647         }
 648 
 649         if (xwindow == null) {
 650             long receiver =
 651                 XDropTargetRegistry.getRegistry().getEmbeddedDropSite(
 652                     xclient.get_window(), x, y);
 653 
 654             if (receiver != 0) {
 655                 XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver);
 656                 if (xbasewindow instanceof XWindow) {
 657                     xwindow = (XWindow)xbasewindow;
 658                 }
 659             }
 660         }
 661 
 662         if (xwindow != null) {
 663             Point p = xwindow.toLocal(x, y);
 664             x = p.x;
 665             y = p.y;
 666         }
 667 
 668         if (xwindow != null) {
 669             notifyProtocolListener(xwindow, x, y, java_action, java_actions,
 670                                    xclient, MouseEvent.MOUSE_RELEASED);
 671         } else if (targetXWindow != null) {
 672             notifyProtocolListener(targetXWindow, x, y,
 673                                    DnDConstants.ACTION_NONE, java_actions,
 674                                    xclient, MouseEvent.MOUSE_EXITED);
 675         }
 676 
 677         return true;
 678     }
 679 
 680     public int getMessageType(XClientMessageEvent xclient) {
 681         if (xclient.get_message_type() !=
 682             MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
 683 
 684             return UNKNOWN_MESSAGE;
 685         }
 686 
 687         long data = xclient.get_data();
 688         byte reason = (byte)(unsafe.getByte(data) &
 689                              MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
 690 
 691         switch (reason) {
 692         case MotifDnDConstants.TOP_LEVEL_ENTER :
 693             return ENTER_MESSAGE;
 694         case MotifDnDConstants.DRAG_MOTION :
 695         case MotifDnDConstants.OPERATION_CHANGED :
 696             return MOTION_MESSAGE;
 697         case MotifDnDConstants.TOP_LEVEL_LEAVE :
 698             return LEAVE_MESSAGE;
 699         case MotifDnDConstants.DROP_START :
 700             return DROP_MESSAGE;
 701         default:
 702             return UNKNOWN_MESSAGE;
 703         }
 704     }
 705 
 706     protected boolean processClientMessageImpl(XClientMessageEvent xclient) {
 707         if (xclient.get_message_type() !=
 708             MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
 709             if (topLevelLeavePostponed) {
 710                 topLevelLeavePostponed = false;
 711                 cleanup();
 712             }
 713 
 714             return false;
 715         }
 716 
 717         long data = xclient.get_data();
 718         byte reason = (byte)(unsafe.getByte(data) &
 719             MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
 720         byte origin = (byte)(unsafe.getByte(data) &
 721             MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
 722 
 723         if (topLevelLeavePostponed) {
 724             topLevelLeavePostponed = false;
 725             if (reason != MotifDnDConstants.DROP_START) {
 726                 cleanup();
 727             }
 728         }
 729 
 730         /* Only initiator messages should be handled. */
 731         if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
 732             return false;
 733         }
 734 
 735         switch (reason) {
 736         case MotifDnDConstants.TOP_LEVEL_ENTER :
 737             return processTopLevelEnter(xclient);
 738         case MotifDnDConstants.DRAG_MOTION :
 739         case MotifDnDConstants.OPERATION_CHANGED :
 740             return processDragMotion(xclient);
 741         case MotifDnDConstants.TOP_LEVEL_LEAVE :
 742             return processTopLevelLeave(xclient);
 743         case MotifDnDConstants.DROP_START :
 744             return processDropStart(xclient);
 745         default:
 746             return false;
 747         }
 748     }
 749 
 750     /*
 751      * Currently we don't synthesize enter/leave messages for Motif DnD
 752      * protocol. See comments in XDropTargetProtocol.postProcessClientMessage.
 753      */
 754     protected void sendEnterMessageToToplevel(long win,
 755                                               XClientMessageEvent xclient) {
 756         throw new Error("UNIMPLEMENTED");
 757     }
 758 
 759     protected void sendLeaveMessageToToplevel(long win,
 760                                               XClientMessageEvent xclient) {
 761         throw new Error("UNIMPLEMENTED");
 762     }
 763 
 764     public boolean forwardEventToEmbedded(long embedded, long ctxt,
 765                                           int eventID) {
 766         // UNIMPLEMENTED.
 767         return false;
 768     }
 769 
 770     public boolean isXEmbedSupported() {
 771         return false;
 772     }
 773 
 774     public boolean sendResponse(long ctxt, int eventID, int action) {
 775         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
 776         if (xclient.get_message_type() !=
 777             MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
 778             return false;
 779         }
 780 
 781         long data = xclient.get_data();
 782         byte reason = (byte)(unsafe.getByte(data) &
 783             MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
 784         byte origin = (byte)(unsafe.getByte(data) &
 785             MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
 786         byte eventByteOrder = unsafe.getByte(data + 1);
 787         byte response_reason = (byte)0;
 788 
 789         /* Only initiator messages should be handled. */
 790         if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
 791             return false;
 792         }
 793 
 794         switch (reason) {
 795         case MotifDnDConstants.TOP_LEVEL_ENTER:
 796         case MotifDnDConstants.TOP_LEVEL_LEAVE:
 797             /* Receiver shouldn't rely to these messages. */
 798             return false;
 799         case MotifDnDConstants.DRAG_MOTION:
 800             switch (eventID) {
 801             case MouseEvent.MOUSE_ENTERED:
 802                 response_reason = MotifDnDConstants.DROP_SITE_ENTER;
 803                 break;
 804             case MouseEvent.MOUSE_DRAGGED:
 805                 response_reason = MotifDnDConstants.DRAG_MOTION;
 806                 break;
 807             case MouseEvent.MOUSE_EXITED:
 808                 response_reason = MotifDnDConstants.DROP_SITE_LEAVE;
 809                 break;
 810             }
 811             break;
 812         case MotifDnDConstants.OPERATION_CHANGED:
 813         case MotifDnDConstants.DROP_START:
 814             response_reason = reason;
 815             break;
 816         default:
 817             // Unknown reason. Shouldn't get here.
 818             assert false;
 819         }
 820 
 821         XClientMessageEvent msg = new XClientMessageEvent();
 822 
 823         try {
 824             msg.set_type(XConstants.ClientMessage);
 825             msg.set_window(MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder));
 826             msg.set_format(8);
 827             msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
 828 
 829             long responseData = msg.get_data();
 830 
 831             unsafe.putByte(responseData, (byte)(response_reason |
 832                            MotifDnDConstants.MOTIF_MESSAGE_FROM_RECEIVER));
 833             unsafe.putByte(responseData + 1, MotifDnDConstants.getByteOrderByte());
 834 
 835             int response_flags = 0;
 836 
 837             if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) {
 838                 short flags = MotifDnDConstants.Swapper.getShort(data + 2,
 839                                                                  eventByteOrder);
 840                 byte dropSiteStatus = (action == DnDConstants.ACTION_NONE) ?
 841                     MotifDnDConstants.MOTIF_INVALID_DROP_SITE :
 842                     MotifDnDConstants.MOTIF_VALID_DROP_SITE;
 843 
 844                 /* Clear action and drop site status bits. */
 845                 response_flags = flags &
 846                     ~MotifDnDConstants.MOTIF_DND_ACTION_MASK &
 847                     ~MotifDnDConstants.MOTIF_DND_STATUS_MASK;
 848                 /* Fill in new action and drop site status. */
 849                 response_flags |=
 850                     MotifDnDConstants.getMotifActionsForJavaActions(action) <<
 851                     MotifDnDConstants.MOTIF_DND_ACTION_SHIFT;
 852                 response_flags |=
 853                     dropSiteStatus << MotifDnDConstants.MOTIF_DND_STATUS_SHIFT;
 854             } else {
 855                 response_flags = 0;
 856             }
 857 
 858             unsafe.putShort(responseData + 2, (short)response_flags);
 859 
 860             /* Write time stamp. */
 861             int time = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder);
 862             unsafe.putInt(responseData + 4, time);
 863 
 864             /* Write coordinates. */
 865             if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) {
 866                 short x = MotifDnDConstants.Swapper.getShort(data + 8,
 867                                                              eventByteOrder);
 868                 short y = MotifDnDConstants.Swapper.getShort(data + 10,
 869                                                              eventByteOrder);
 870                 unsafe.putShort(responseData + 8, x); // x
 871                 unsafe.putShort(responseData + 10, y); // y
 872             } else {
 873                 unsafe.putShort(responseData + 8, (short)0); // x
 874                 unsafe.putShort(responseData + 10, (short)0); // y
 875             }
 876 
 877             XToolkit.awtLock();
 878             try {
 879                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
 880                                        msg.get_window(),
 881                                        false, XConstants.NoEventMask,
 882                                        msg.pData);
 883             } finally {
 884                 XToolkit.awtUnlock();
 885             }
 886         } finally {
 887             msg.dispose();
 888         }
 889 
 890         return true;
 891     }
 892 
 893     public Object getData(long ctxt, long format)
 894       throws IllegalArgumentException, IOException {
 895         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
 896 
 897         if (xclient.get_message_type() !=
 898             MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
 899             throw new IllegalArgumentException();
 900         }
 901 
 902         long data = xclient.get_data();
 903         byte reason = (byte)(unsafe.getByte(data) &
 904             MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
 905         byte origin = (byte)(unsafe.getByte(data) &
 906             MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
 907         byte eventByteOrder = unsafe.getByte(data + 1);
 908 
 909         if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
 910             throw new IOException("Cannot get data: corrupted context");
 911         }
 912 
 913         long selatom = 0;
 914 
 915         switch (reason) {
 916         case MotifDnDConstants.DRAG_MOTION :
 917         case MotifDnDConstants.OPERATION_CHANGED :
 918             selatom = sourceAtom;
 919             break;
 920         case MotifDnDConstants.DROP_START :
 921             selatom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
 922             break;
 923         default:
 924             throw new IOException("Cannot get data: invalid message reason");
 925         }
 926 
 927         if (selatom == 0) {
 928             throw new IOException("Cannot get data: drag source property atom unavailable");
 929         }
 930 
 931         long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL;
 932                           // with correction of (32-bit unsigned to 64-bit signed) implicit conversion.
 933 
 934         XAtom selectionAtom = XAtom.get(selatom);
 935 
 936         XSelection selection = XSelection.getSelection(selectionAtom);
 937         if (selection == null) {
 938             selection = new XSelection(selectionAtom);
 939         }
 940 
 941         return selection.getData(format, time_stamp);
 942     }
 943 
 944     public boolean sendDropDone(long ctxt, boolean success, int dropAction) {
 945         XClientMessageEvent xclient = new XClientMessageEvent(ctxt);
 946 
 947         if (xclient.get_message_type() !=
 948             MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) {
 949             return false;
 950         }
 951 
 952         long data = xclient.get_data();
 953         byte reason = (byte)(unsafe.getByte(data) &
 954             MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK);
 955         byte origin = (byte)(unsafe.getByte(data) &
 956             MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK);
 957         byte eventByteOrder = unsafe.getByte(data + 1);
 958 
 959         if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) {
 960             return false;
 961         }
 962 
 963         if (reason != MotifDnDConstants.DROP_START) {
 964             return false;
 965         }
 966 
 967         long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL;
 968                           // with correction of (32-bit unsigned to 64-bit signed) implicit conversion.
 969 
 970         long sel_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder);
 971 
 972         long status_atom = 0;
 973 
 974         if (success) {
 975             status_atom = MotifDnDConstants.XA_XmTRANSFER_SUCCESS.getAtom();
 976         } else {
 977             status_atom = MotifDnDConstants.XA_XmTRANSFER_FAILURE.getAtom();
 978         }
 979 
 980         XToolkit.awtLock();
 981         try {
 982             XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
 983                                           sel_atom,
 984                                           status_atom,
 985                                           MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom(),
 986                                           XWindow.getXAWTRootWindow().getWindow(),
 987                                           time_stamp);
 988 
 989             /*
 990              * Flush the buffer to guarantee that the drop completion event is sent
 991              * to the source before the method returns.
 992              */
 993             XlibWrapper.XFlush(XToolkit.getDisplay());
 994         } finally {
 995             XToolkit.awtUnlock();
 996         }
 997 
 998         /* Trick to prevent cleanup() from posting dragExit */
 999         targetXWindow = null;
1000 
1001         /* Cannot do cleanup before the drop finishes as we may need
1002            source protocol version to send drop finished message. */
1003         cleanup();
1004         return true;
1005     }
1006 
1007     public final long getSourceWindow() {
1008         return sourceWindow;
1009     }
1010 
1011     /**
1012      * Reset the state of the object.
1013      */
1014     public void cleanup() {
1015         // Clear the reference to this protocol.
1016         XDropTargetEventProcessor.reset();
1017 
1018         if (targetXWindow != null) {
1019             notifyProtocolListener(targetXWindow, 0, 0,
1020                                    DnDConstants.ACTION_NONE, sourceActions,
1021                                    null, MouseEvent.MOUSE_EXITED);
1022         }
1023 
1024         if (sourceWindow != 0) {
1025             XToolkit.awtLock();
1026             try {
1027                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
1028                 XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow,
1029                                          sourceWindowMask);
1030                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
1031             } finally {
1032                 XToolkit.awtUnlock();
1033             }
1034         }
1035 
1036         sourceWindow = 0;
1037         sourceWindowMask = 0;
1038         sourceProtocolVersion = 0;
1039         sourceActions = DnDConstants.ACTION_NONE;
1040         sourceFormats = null;
1041         sourceAtom = 0;
1042         userAction = DnDConstants.ACTION_NONE;
1043         sourceX = 0;
1044         sourceY = 0;
1045         targetXWindow = null;
1046         topLevelLeavePostponed = false;
1047     }
1048 
1049     public boolean isDragOverComponent() {
1050         return targetXWindow != null;
1051     }
1052 
1053     private void notifyProtocolListener(XWindow xwindow, int x, int y,
1054                                         int dropAction, int actions,
1055                                         XClientMessageEvent xclient,
1056                                         int eventID) {
1057         long nativeCtxt = 0;
1058 
1059         // Make a copy of the passed XClientMessageEvent structure, since
1060         // the original structure can be freed before this
1061         // SunDropTargetEvent is dispatched.
1062         if (xclient != null) {
1063             int size = XClientMessageEvent.getSize();
1064 
1065             nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
1066 
1067             unsafe.copyMemory(xclient.pData, nativeCtxt, size);
1068         }
1069 
1070         getProtocolListener().handleDropTargetNotification(xwindow, x, y,
1071                                                            dropAction,
1072                                                            actions,
1073                                                            sourceFormats,
1074                                                            nativeCtxt,
1075                                                            eventID);
1076     }
1077 }