1 /*
   2  * Copyright (c) 2003, 2015, 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.Component;
  29 import java.awt.Cursor;
  30 import java.awt.Window;
  31 
  32 import java.awt.datatransfer.DataFlavor;
  33 import java.awt.datatransfer.Transferable;
  34 
  35 import java.awt.dnd.DnDConstants;
  36 import java.awt.dnd.DragGestureEvent;
  37 import java.awt.dnd.InvalidDnDOperationException;
  38 
  39 import java.util.*;
  40 
  41 import sun.java2d.pipe.Region;
  42 import sun.util.logging.PlatformLogger;
  43 
  44 import sun.awt.dnd.SunDragSourceContextPeer;
  45 import sun.awt.dnd.SunDropTargetContextPeer;
  46 import sun.awt.SunToolkit;
  47 import sun.awt.AWTAccessor;
  48 
  49 /**
  50  * The XDragSourceContextPeer class is the class responsible for handling
  51  * the interaction between the XDnD/Motif DnD subsystem and Java drag sources.
  52  *
  53  * @since 1.5
  54  */
  55 public final class XDragSourceContextPeer
  56     extends SunDragSourceContextPeer implements XDragSourceProtocolListener {
  57     private static final PlatformLogger logger =
  58         PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDragSourceContextPeer");
  59 
  60     /* The events selected on the root window when the drag begins. */
  61     private static final int ROOT_EVENT_MASK = (int)XConstants.ButtonMotionMask |
  62         (int)XConstants.KeyPressMask | (int)XConstants.KeyReleaseMask;
  63     /* The events to be delivered during grab. */
  64     private static final int GRAB_EVENT_MASK = (int)XConstants.ButtonPressMask |
  65         (int)XConstants.ButtonMotionMask | (int)XConstants.ButtonReleaseMask;
  66 
  67     /* The event mask of the root window before the drag operation starts. */
  68     private long rootEventMask = 0;
  69     private boolean dndInProgress = false;
  70     private boolean dragInProgress = false;
  71     private long dragRootWindow = 0;
  72 
  73     /* The protocol chosen for the communication with the current drop target. */
  74     private XDragSourceProtocol dragProtocol = null;
  75     /* The drop action chosen by the current drop target. */
  76     private int targetAction = DnDConstants.ACTION_NONE;
  77     /* The set of drop actions supported by the drag source. */
  78     private int sourceActions = DnDConstants.ACTION_NONE;
  79     /* The drop action selected by the drag source based on the modifiers state
  80        and the action selected by the current drop target. */
  81     private int sourceAction = DnDConstants.ACTION_NONE;
  82     /* The data formats supported by the drag source for the current drag
  83        operation. */
  84     private long[] sourceFormats = null;
  85     /* The XID of the root subwindow that contains the current target. */
  86     private long targetRootSubwindow = 0;
  87     /* window scale factor */
  88     int windowScale = 1;
  89     /* The pointer location. */
  90     private int xRoot = 0;
  91     private int yRoot = 0;
  92     /* Keyboard modifiers state. */
  93     private int eventState = 0;
  94 
  95     /* XEmbed DnD support. We act as a proxy between source and target. */
  96     private long proxyModeSourceWindow = 0;
  97 
  98     /* The singleton instance. */
  99     private static final XDragSourceContextPeer theInstance =
 100         new XDragSourceContextPeer(null);
 101 
 102     private XDragSourceContextPeer(DragGestureEvent dge) {
 103         super(dge);
 104     }
 105 
 106     static XDragSourceProtocolListener getXDragSourceProtocolListener() {
 107         return theInstance;
 108     }
 109 
 110     static XDragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge)
 111       throws InvalidDnDOperationException {
 112     theInstance.setTrigger(dge);
 113         return theInstance;
 114     }
 115 
 116     protected void startDrag(Transferable transferable,
 117                              long[] formats, Map<Long, DataFlavor> formatMap) {
 118         Component component = getTrigger().getComponent();
 119         Component c = null;
 120         XWindowPeer wpeer = null;
 121 
 122         for (c = component; c != null && !(c instanceof Window);
 123              c = AWTAccessor.getComponentAccessor().getParent(c));
 124 
 125         if (c instanceof Window) {
 126             wpeer = AWTAccessor.getComponentAccessor().getPeer(c);
 127         }
 128 
 129         if (wpeer == null) {
 130             throw new InvalidDnDOperationException(
 131                 "Cannot find top-level for the drag source component");
 132         }
 133 
 134         long xcursor = 0;
 135         long rootWindow = 0;
 136         long timeStamp = 0;
 137         windowScale = wpeer.getScale();
 138 
 139         /* Retrieve the X cursor for the drag operation. */
 140         {
 141             Cursor cursor = getCursor();
 142             if (cursor != null) {
 143                 xcursor = XGlobalCursorManager.getCursor(cursor);
 144             }
 145         }
 146 
 147         XToolkit.awtLock();
 148         try {
 149             if (proxyModeSourceWindow != 0) {
 150                 throw new InvalidDnDOperationException("Proxy drag in progress");
 151             }
 152             if (dndInProgress) {
 153                 throw new InvalidDnDOperationException("Drag in progress");
 154             }
 155 
 156             /* Determine the root window for the drag operation. */
 157             {
 158                 long screen = XlibWrapper.XScreenNumberOfScreen(wpeer.getScreen());
 159                 rootWindow = XlibWrapper.RootWindow(XToolkit.getDisplay(), screen);
 160             }
 161 
 162             timeStamp = XToolkit.getCurrentServerTime();
 163 
 164             int dropActions = getDragSourceContext().getSourceActions();
 165 
 166             Iterator<XDragSourceProtocol> dragProtocols =
 167                 XDragAndDropProtocols.getDragSourceProtocols();
 168             while (dragProtocols.hasNext()) {
 169                 XDragSourceProtocol dragProtocol = dragProtocols.next();
 170                 try {
 171                     dragProtocol.initializeDrag(dropActions, transferable,
 172                                                 formatMap, formats);
 173                 } catch (XException xe) {
 174                     throw (InvalidDnDOperationException)
 175                         new InvalidDnDOperationException().initCause(xe);
 176                 }
 177             }
 178 
 179             /* Install X grabs. */
 180             {
 181                 int status;
 182                 XWindowAttributes wattr = new XWindowAttributes();
 183                 try {
 184                     status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 185                                                               rootWindow, wattr.pData);
 186 
 187                     if (status == 0) {
 188                         throw new InvalidDnDOperationException("XGetWindowAttributes failed");
 189                     }
 190 
 191                     rootEventMask = wattr.get_your_event_mask();
 192 
 193                     XlibWrapper.XSelectInput(XToolkit.getDisplay(), rootWindow,
 194                                              rootEventMask | ROOT_EVENT_MASK);
 195                 } finally {
 196                     wattr.dispose();
 197                 }
 198 
 199                 XBaseWindow.ungrabInput();
 200 
 201                 status = XlibWrapper.XGrabPointer(XToolkit.getDisplay(), rootWindow,
 202                                                   0, GRAB_EVENT_MASK,
 203                                                   XConstants.GrabModeAsync,
 204                                                   XConstants.GrabModeAsync,
 205                                                   XConstants.None, xcursor, timeStamp);
 206 
 207                 if (status != XConstants.GrabSuccess) {
 208                     cleanup(timeStamp);
 209                     throwGrabFailureException("Cannot grab pointer", status);
 210                     return;
 211                 }
 212 
 213                 status = XlibWrapper.XGrabKeyboard(XToolkit.getDisplay(), rootWindow,
 214                                                    0,
 215                                                    XConstants.GrabModeAsync,
 216                                                    XConstants.GrabModeAsync,
 217                                                    timeStamp);
 218 
 219                 if (status != XConstants.GrabSuccess) {
 220                     cleanup(timeStamp);
 221                     throwGrabFailureException("Cannot grab keyboard", status);
 222                     return;
 223                 }
 224             }
 225 
 226             /* Update the global state. */
 227             dndInProgress = true;
 228             dragInProgress = true;
 229             dragRootWindow = rootWindow;
 230             sourceActions = dropActions;
 231             sourceFormats = formats;
 232         } finally {
 233             XToolkit.awtUnlock();
 234         }
 235 
 236         /* This implementation doesn't use native context */
 237         setNativeContext(0);
 238 
 239         SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(transferable);
 240     }
 241 
 242     public long getProxyModeSourceWindow() {
 243         return proxyModeSourceWindow;
 244     }
 245 
 246     private void setProxyModeSourceWindowImpl(long window) {
 247         proxyModeSourceWindow = window;
 248     }
 249 
 250     public static void setProxyModeSourceWindow(long window) {
 251         theInstance.setProxyModeSourceWindowImpl(window);
 252     }
 253 
 254     /**
 255      * set cursor
 256      */
 257 
 258     public void setCursor(Cursor c) throws InvalidDnDOperationException {
 259         XToolkit.awtLock();
 260         try {
 261             super.setCursor(c);
 262         } finally {
 263             XToolkit.awtUnlock();
 264         }
 265     }
 266 
 267     protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) {
 268         assert XToolkit.isAWTLockHeldByCurrentThread();
 269 
 270         if (c == null) {
 271             return;
 272         }
 273 
 274         long xcursor = XGlobalCursorManager.getCursor(c);
 275 
 276         if (xcursor == 0) {
 277             return;
 278         }
 279 
 280         XlibWrapper.XChangeActivePointerGrab(XToolkit.getDisplay(),
 281                                              GRAB_EVENT_MASK,
 282                                              xcursor,
 283                                              XConstants.CurrentTime);
 284     }
 285 
 286     protected boolean needsBogusExitBeforeDrop() {
 287         return false;
 288     }
 289 
 290     private void throwGrabFailureException(String msg, int grabStatus)
 291       throws InvalidDnDOperationException {
 292         String msgCause = "";
 293         switch (grabStatus) {
 294         case XConstants.GrabNotViewable:  msgCause = "not viewable";    break;
 295         case XConstants.AlreadyGrabbed:   msgCause = "already grabbed"; break;
 296         case XConstants.GrabInvalidTime:  msgCause = "invalid time";    break;
 297         case XConstants.GrabFrozen:       msgCause = "grab frozen";     break;
 298         default:                           msgCause = "unknown failure"; break;
 299         }
 300         throw new InvalidDnDOperationException(msg + ": " + msgCause);
 301     }
 302 
 303     /**
 304      * The caller must own awtLock.
 305      */
 306     public void cleanup(long time) {
 307         if (dndInProgress) {
 308             if (dragProtocol != null) {
 309                 dragProtocol.sendLeaveMessage(time);
 310             }
 311 
 312             if (targetAction != DnDConstants.ACTION_NONE) {
 313                 dragExit(xRoot, yRoot);
 314             }
 315 
 316             dragDropFinished(false, DnDConstants.ACTION_NONE, xRoot, yRoot);
 317         }
 318 
 319         Iterator<XDragSourceProtocol> dragProtocols =
 320             XDragAndDropProtocols.getDragSourceProtocols();
 321         while (dragProtocols.hasNext()) {
 322             XDragSourceProtocol dragProtocol = dragProtocols.next();
 323             try {
 324                 dragProtocol.cleanup();
 325             } catch (XException xe) {
 326                 // Ignore the exception.
 327             }
 328         }
 329 
 330         dndInProgress = false;
 331         dragInProgress = false;
 332         dragRootWindow = 0;
 333         sourceFormats = null;
 334         sourceActions = DnDConstants.ACTION_NONE;
 335         sourceAction = DnDConstants.ACTION_NONE;
 336         eventState = 0;
 337         xRoot = 0;
 338         yRoot = 0;
 339 
 340         cleanupTargetInfo();
 341 
 342         removeDnDGrab(time);
 343     }
 344 
 345     /**
 346      * The caller must own awtLock.
 347      */
 348     private void cleanupTargetInfo() {
 349         targetAction = DnDConstants.ACTION_NONE;
 350         dragProtocol = null;
 351         targetRootSubwindow = 0;
 352     }
 353 
 354     private void removeDnDGrab(long time) {
 355         assert XToolkit.isAWTLockHeldByCurrentThread();
 356 
 357         XlibWrapper.XUngrabPointer(XToolkit.getDisplay(), time);
 358         XlibWrapper.XUngrabKeyboard(XToolkit.getDisplay(), time);
 359 
 360         /* Restore the root event mask if it was changed. */
 361         if ((rootEventMask | ROOT_EVENT_MASK) != rootEventMask &&
 362             dragRootWindow != 0) {
 363 
 364             XlibWrapper.XSelectInput(XToolkit.getDisplay(),
 365                                      dragRootWindow,
 366                                      rootEventMask);
 367         }
 368 
 369         rootEventMask = 0;
 370         dragRootWindow = 0;
 371     }
 372 
 373     private boolean processClientMessage(XClientMessageEvent xclient) {
 374         if (dragProtocol != null) {
 375             return dragProtocol.processClientMessage(xclient);
 376         }
 377         return false;
 378     }
 379 
 380     /**
 381      * Updates the source action according to the specified state.
 382      *
 383      * @return true if the source
 384      */
 385     private boolean updateSourceAction(int state) {
 386         int action = SunDragSourceContextPeer.convertModifiersToDropAction(XWindow.getModifiers(state, 0, 0),
 387                                                                            sourceActions);
 388         if (sourceAction == action) {
 389             return false;
 390         }
 391         sourceAction = action;
 392         return true;
 393     }
 394 
 395     /**
 396      * Returns the client window under the specified root subwindow.
 397      */
 398     private static long findClientWindow(long window) {
 399         if (XlibUtil.isTrueToplevelWindow(window)) {
 400             return window;
 401         }
 402 
 403         Set<Long> children = XlibUtil.getChildWindows(window);
 404         for (Long child : children) {
 405             long win = findClientWindow(child);
 406             if (win != 0) {
 407                 return win;
 408             }
 409         }
 410 
 411         return 0;
 412     }
 413 
 414     private void doUpdateTargetWindow(long subwindow, long time) {
 415         long clientWindow = 0;
 416         long proxyWindow = 0;
 417         XDragSourceProtocol protocol = null;
 418         boolean isReceiver = false;
 419 
 420         if (subwindow != 0) {
 421             clientWindow = findClientWindow(subwindow);
 422         }
 423 
 424         if (clientWindow != 0) {
 425             Iterator<XDragSourceProtocol> dragProtocols =
 426                 XDragAndDropProtocols.getDragSourceProtocols();
 427             while (dragProtocols.hasNext()) {
 428                 XDragSourceProtocol dragProtocol = dragProtocols.next();
 429                 if (dragProtocol.attachTargetWindow(clientWindow, time)) {
 430                     protocol = dragProtocol;
 431                     break;
 432                 }
 433             }
 434         }
 435 
 436         /* Update the global state. */
 437         dragProtocol = protocol;
 438         targetAction = DnDConstants.ACTION_NONE;
 439         targetRootSubwindow = subwindow;
 440     }
 441 
 442     private void updateTargetWindow(XMotionEvent xmotion) {
 443         assert XToolkit.isAWTLockHeldByCurrentThread();
 444 
 445         int x = scaleDown(xmotion.get_x_root());
 446         int y = scaleDown(xmotion.get_y_root());
 447         long time = xmotion.get_time();
 448         long subwindow = xmotion.get_subwindow();
 449 
 450         /*
 451          * If this event had occurred before the pointer was grabbed,
 452          * query the server for the current root subwindow.
 453          */
 454         if (xmotion.get_window() != xmotion.get_root()) {
 455             XlibWrapper.XQueryPointer(XToolkit.getDisplay(),
 456                                       xmotion.get_root(),
 457                                       XlibWrapper.larg1,  // root
 458                                       XlibWrapper.larg2,  // subwindow
 459                                       XlibWrapper.larg3,  // x_root
 460                                       XlibWrapper.larg4,  // y_root
 461                                       XlibWrapper.larg5,  // x
 462                                       XlibWrapper.larg6,  // y
 463                                       XlibWrapper.larg7); // modifiers
 464             subwindow = Native.getLong(XlibWrapper.larg2);
 465         }
 466 
 467         if (targetRootSubwindow != subwindow) {
 468             if (dragProtocol != null) {
 469                 dragProtocol.sendLeaveMessage(time);
 470 
 471                 /*
 472                  * Neither Motif DnD nor XDnD provide a mean for the target
 473                  * to notify the source that the pointer exits the drop site
 474                  * that occupies the whole top level.
 475                  * We detect this situation and post dragExit.
 476                  */
 477                 if (targetAction != DnDConstants.ACTION_NONE) {
 478                     dragExit(x, y);
 479                 }
 480             }
 481 
 482             /* Update the global state. */
 483             doUpdateTargetWindow(subwindow, time);
 484 
 485             if (dragProtocol != null) {
 486                 dragProtocol.sendEnterMessage(sourceFormats,
 487                                               sourceAction,
 488                                               sourceActions,
 489                                               time);
 490             }
 491         }
 492     }
 493 
 494     /*
 495      * DO NOT USE is_hint field of xmotion since it could not be set when we
 496      * convert XKeyEvent or XButtonRelease to XMotionEvent.
 497      */
 498     private void processMouseMove(XMotionEvent xmotion) {
 499         if (!dragInProgress) {
 500             return;
 501         }
 502 
 503         int motionXRoot = scaleDown(xmotion.get_x_root());
 504         int motionYRoot = scaleDown(xmotion.get_y_root());
 505 
 506         if (xRoot != motionXRoot || yRoot != motionYRoot) {
 507             xRoot = motionXRoot;
 508             yRoot = motionYRoot;
 509 
 510             postDragSourceDragEvent(targetAction,
 511                                     XWindow.getModifiers(xmotion.get_state(),0,0),
 512                                     xRoot, yRoot, DISPATCH_MOUSE_MOVED);
 513         }
 514 
 515         if (eventState != xmotion.get_state()) {
 516             if (updateSourceAction(xmotion.get_state()) && dragProtocol != null) {
 517                 postDragSourceDragEvent(targetAction,
 518                                         XWindow.getModifiers(xmotion.get_state(),0,0),
 519                                         xRoot, yRoot, DISPATCH_CHANGED);
 520             }
 521             eventState = xmotion.get_state();
 522         }
 523 
 524         updateTargetWindow(xmotion);
 525 
 526         if (dragProtocol != null) {
 527             dragProtocol.sendMoveMessage(scaleDown(xmotion.get_x_root()),
 528                                          scaleDown(xmotion.get_y_root()),
 529                                          sourceAction, sourceActions,
 530                                          xmotion.get_time());
 531         }
 532     }
 533 
 534     private void processDrop(XButtonEvent xbutton) {
 535         try {
 536             dragProtocol.initiateDrop(scaleDown(xbutton.get_x_root()),
 537                                       scaleDown(xbutton.get_y_root()),
 538                                       sourceAction, sourceActions,
 539                                       xbutton.get_time());
 540         } catch (XException e) {
 541             cleanup(xbutton.get_time());
 542         }
 543     }
 544 
 545     private boolean processProxyModeEvent(XEvent ev) {
 546         if (getProxyModeSourceWindow() == 0) {
 547             return false;
 548         }
 549 
 550         if (ev.get_type() != XConstants.ClientMessage) {
 551             return false;
 552         }
 553 
 554         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 555             logger.finest("        proxyModeSourceWindow=" +
 556                           getProxyModeSourceWindow() +
 557                           " ev=" + ev);
 558         }
 559 
 560         XClientMessageEvent xclient = ev.get_xclient();
 561 
 562         Iterator<XDragSourceProtocol> dragProtocols =
 563             XDragAndDropProtocols.getDragSourceProtocols();
 564         while (dragProtocols.hasNext()) {
 565             XDragSourceProtocol dragProtocol = dragProtocols.next();
 566             if (dragProtocol.processProxyModeEvent(xclient,
 567                                                    getProxyModeSourceWindow())) {
 568                 return true;
 569             }
 570         }
 571 
 572         return false;
 573     }
 574 
 575     /**
 576      * The caller must own awtLock.
 577      *
 578      * @return true if the event was processed and shouldn't be passed along.
 579      */
 580     private boolean doProcessEvent(XEvent ev) {
 581         assert XToolkit.isAWTLockHeldByCurrentThread();
 582 
 583         if (processProxyModeEvent(ev)) {
 584             return true;
 585         }
 586 
 587         if (!dndInProgress) {
 588             return false;
 589         }
 590 
 591         switch (ev.get_type()) {
 592         case XConstants.ClientMessage: {
 593             XClientMessageEvent xclient = ev.get_xclient();
 594             return processClientMessage(xclient);
 595         }
 596         case XConstants.DestroyNotify: {
 597             XDestroyWindowEvent xde = ev.get_xdestroywindow();
 598 
 599             /* Target crashed during drop processing - cleanup. */
 600             if (!dragInProgress &&
 601                 dragProtocol != null &&
 602                 xde.get_window() == dragProtocol.getTargetWindow()) {
 603                 cleanup(XConstants.CurrentTime);
 604                 return true;
 605             }
 606             /* Pass along */
 607             return false;
 608         }
 609         }
 610 
 611         if (!dragInProgress) {
 612             return false;
 613         }
 614 
 615         /* Process drag-only messages. */
 616         switch (ev.get_type()) {
 617         case XConstants.KeyRelease:
 618         case XConstants.KeyPress: {
 619             XKeyEvent xkey = ev.get_xkey();
 620             long keysym = XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(),
 621                                                        xkey.get_keycode(), 0);
 622             switch ((int)keysym) {
 623             case (int)XKeySymConstants.XK_Escape: {
 624                 if (ev.get_type() == XConstants.KeyRelease) {
 625                     cleanup(xkey.get_time());
 626                 }
 627                 break;
 628             }
 629             case (int)XKeySymConstants.XK_Control_R:
 630             case (int)XKeySymConstants.XK_Control_L:
 631             case (int)XKeySymConstants.XK_Shift_R:
 632             case (int)XKeySymConstants.XK_Shift_L: {
 633                 XlibWrapper.XQueryPointer(XToolkit.getDisplay(),
 634                                           xkey.get_root(),
 635                                           XlibWrapper.larg1,  // root
 636                                           XlibWrapper.larg2,  // subwindow
 637                                           XlibWrapper.larg3,  // x_root
 638                                           XlibWrapper.larg4,  // y_root
 639                                           XlibWrapper.larg5,  // x
 640                                           XlibWrapper.larg6,  // y
 641                                           XlibWrapper.larg7); // modifiers
 642                 XMotionEvent xmotion = new XMotionEvent();
 643                 try {
 644                     xmotion.set_type(XConstants.MotionNotify);
 645                     xmotion.set_serial(xkey.get_serial());
 646                     xmotion.set_send_event(xkey.get_send_event());
 647                     xmotion.set_display(xkey.get_display());
 648                     xmotion.set_window(xkey.get_window());
 649                     xmotion.set_root(xkey.get_root());
 650                     xmotion.set_subwindow(xkey.get_subwindow());
 651                     xmotion.set_time(xkey.get_time());
 652                     xmotion.set_x(xkey.get_x());
 653                     xmotion.set_y(xkey.get_y());
 654                     xmotion.set_x_root(xkey.get_x_root());
 655                     xmotion.set_y_root(xkey.get_y_root());
 656                     xmotion.set_state((int)Native.getLong(XlibWrapper.larg7));
 657                     // we do not use this field, so it's unset for now
 658                     // xmotion.set_is_hint(???);
 659                     xmotion.set_same_screen(xkey.get_same_screen());
 660 
 661                     //It's safe to use key event as motion event since we use only their common fields.
 662                     processMouseMove(xmotion);
 663                 } finally {
 664                     xmotion.dispose();
 665                 }
 666                 break;
 667             }
 668             }
 669             return true;
 670         }
 671         case XConstants.ButtonPress:
 672             return true;
 673         case XConstants.MotionNotify:
 674             processMouseMove(ev.get_xmotion());
 675             return true;
 676         case XConstants.ButtonRelease: {
 677             XButtonEvent xbutton = ev.get_xbutton();
 678             /*
 679              * Ignore the buttons above 20 due to the bit limit for
 680              * InputEvent.BUTTON_DOWN_MASK.
 681              * One more bit is reserved for FIRST_HIGH_BIT.
 682              */
 683             if (xbutton.get_button() > SunToolkit.MAX_BUTTONS_SUPPORTED) {
 684                 return true;
 685             }
 686 
 687             /*
 688              * On some X servers it could happen that ButtonRelease coordinates
 689              * differ from the latest MotionNotify coordinates, so we need to
 690              * process it as a mouse motion.
 691              */
 692             XMotionEvent xmotion = new XMotionEvent();
 693             try {
 694                 xmotion.set_type(XConstants.MotionNotify);
 695                 xmotion.set_serial(xbutton.get_serial());
 696                 xmotion.set_send_event(xbutton.get_send_event());
 697                 xmotion.set_display(xbutton.get_display());
 698                 xmotion.set_window(xbutton.get_window());
 699                 xmotion.set_root(xbutton.get_root());
 700                 xmotion.set_subwindow(xbutton.get_subwindow());
 701                 xmotion.set_time(xbutton.get_time());
 702                 xmotion.set_x(xbutton.get_x());
 703                 xmotion.set_y(xbutton.get_y());
 704                 xmotion.set_x_root(xbutton.get_x_root());
 705                 xmotion.set_y_root(xbutton.get_y_root());
 706                 xmotion.set_state(xbutton.get_state());
 707                 // we do not use this field, so it's unset for now
 708                 // xmotion.set_is_hint(???);
 709                 xmotion.set_same_screen(xbutton.get_same_screen());
 710 
 711                 //It's safe to use key event as motion event since we use only their common fields.
 712                 processMouseMove(xmotion);
 713             } finally {
 714                 xmotion.dispose();
 715             }
 716             if (xbutton.get_button() == XConstants.buttons[0]
 717                 || xbutton.get_button() == XConstants.buttons[1]) {
 718                 // drag is initiated with Button1 or Button2 pressed and
 719                 // ended on release of either of these buttons (as the same
 720                 // behavior was with our old Motif DnD-based implementation)
 721                 removeDnDGrab(xbutton.get_time());
 722                 dragInProgress = false;
 723                 if (dragProtocol != null && targetAction != DnDConstants.ACTION_NONE) {
 724                     /*
 725                      * ACTION_NONE indicates that either the drop target rejects the
 726                      * drop or it haven't responded yet. The latter could happen in
 727                      * case of fast drag, slow target-server connection or slow
 728                      * drag notifications processing on the target side.
 729                      */
 730                     processDrop(xbutton);
 731                 } else {
 732                     cleanup(xbutton.get_time());
 733                 }
 734             }
 735             return true;
 736         }
 737         }
 738 
 739         return false;
 740     }
 741 
 742     static boolean processEvent(XEvent ev) {
 743         XToolkit.awtLock();
 744         try {
 745             try {
 746                 return theInstance.doProcessEvent(ev);
 747             } catch (XException e) {
 748                 e.printStackTrace();
 749                 return false;
 750             }
 751         } finally {
 752             XToolkit.awtUnlock();
 753         }
 754     }
 755 
 756     /* XDragSourceProtocolListener implementation */
 757 
 758     public void handleDragReply(int action) {
 759         // NOTE: we have to use the current pointer location, since
 760         // the target didn't specify the coordinates for the reply.
 761         handleDragReply(action, xRoot, yRoot);
 762     }
 763 
 764     public void handleDragReply(int action, int x, int y) {
 765         // NOTE: we have to use the current modifiers state, since
 766         // the target didn't specify the modifiers state for the reply.
 767         handleDragReply(action, xRoot, yRoot, XWindow.getModifiers(eventState,0,0));
 768     }
 769 
 770     public void handleDragReply(int action, int x, int y, int modifiers) {
 771         if (action == DnDConstants.ACTION_NONE &&
 772             targetAction != DnDConstants.ACTION_NONE) {
 773             dragExit(x, y);
 774         } else if (action != DnDConstants.ACTION_NONE) {
 775             int type = 0;
 776 
 777             if (targetAction == DnDConstants.ACTION_NONE) {
 778                 type = SunDragSourceContextPeer.DISPATCH_ENTER;
 779             } else {
 780                 type = SunDragSourceContextPeer.DISPATCH_MOTION;
 781             }
 782 
 783             // Note that we use the modifiers state a
 784             postDragSourceDragEvent(action, modifiers, x, y, type);
 785         }
 786 
 787         targetAction = action;
 788     }
 789 
 790     public void handleDragFinished() {
 791         /* Assume that the drop was successful. */
 792         handleDragFinished(true);
 793     }
 794 
 795     public void handleDragFinished(boolean success) {
 796         /* Assume that the performed drop action is the latest drop action
 797            accepted by the drop target. */
 798         handleDragFinished(true, targetAction);
 799     }
 800 
 801     public void handleDragFinished(boolean success, int action) {
 802         // NOTE: we have to use the current pointer location, since
 803         // the target didn't specify the coordinates for the reply.
 804         handleDragFinished(success, action, xRoot, yRoot);
 805     }
 806 
 807     public void handleDragFinished(boolean success, int action, int x, int y) {
 808         dragDropFinished(success, action, x, y);
 809 
 810         dndInProgress = false;
 811         cleanup(XConstants.CurrentTime);
 812     }
 813 
 814     public int scaleUp(int x) {
 815         return Region.clipRound(x * (double)windowScale);
 816     }
 817 
 818     public int scaleDown(int x) {
 819         return Region.clipRound(x / (double)windowScale);
 820     }
 821 }