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 
XDragSourceContextPeer(DragGestureEvent dge)102     private XDragSourceContextPeer(DragGestureEvent dge) {
103         super(dge);
104     }
105 
getXDragSourceProtocolListener()106     static XDragSourceProtocolListener getXDragSourceProtocolListener() {
107         return theInstance;
108     }
109 
createDragSourceContextPeer(DragGestureEvent dge)110     static XDragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge)
111       throws InvalidDnDOperationException {
112     theInstance.setTrigger(dge);
113         return theInstance;
114     }
115 
startDrag(Transferable transferable, long[] formats, Map<Long, DataFlavor> formatMap)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 
getProxyModeSourceWindow()242     public long getProxyModeSourceWindow() {
243         return proxyModeSourceWindow;
244     }
245 
setProxyModeSourceWindowImpl(long window)246     private void setProxyModeSourceWindowImpl(long window) {
247         proxyModeSourceWindow = window;
248     }
249 
setProxyModeSourceWindow(long window)250     public static void setProxyModeSourceWindow(long window) {
251         theInstance.setProxyModeSourceWindowImpl(window);
252     }
253 
254     /**
255      * set cursor
256      */
257 
setCursor(Cursor c)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 
setNativeCursor(long nativeCtxt, Cursor c, int cType)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 
needsBogusExitBeforeDrop()286     protected boolean needsBogusExitBeforeDrop() {
287         return false;
288     }
289 
throwGrabFailureException(String msg, int grabStatus)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      */
cleanup(long time)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      */
cleanupTargetInfo()348     private void cleanupTargetInfo() {
349         targetAction = DnDConstants.ACTION_NONE;
350         dragProtocol = null;
351         targetRootSubwindow = 0;
352     }
353 
removeDnDGrab(long time)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 
processClientMessage(XClientMessageEvent xclient)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      */
updateSourceAction(int state)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      */
findClientWindow(long window)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 
doUpdateTargetWindow(long subwindow, long time)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 
updateTargetWindow(XMotionEvent xmotion)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      */
processMouseMove(XMotionEvent xmotion)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 
processDrop(XButtonEvent xbutton)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 
processProxyModeEvent(XEvent ev)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      */
doProcessEvent(XEvent ev)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(Native.getInt(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 
processEvent(XEvent ev)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 
handleDragReply(int action)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 
handleDragReply(int action, int x, int y)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 
handleDragReply(int action, int x, int y, int modifiers)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 
handleDragFinished()790     public void handleDragFinished() {
791         /* Assume that the drop was successful. */
792         handleDragFinished(true);
793     }
794 
handleDragFinished(boolean success)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 
handleDragFinished(boolean success, int action)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 
handleDragFinished(boolean success, int action, int x, int y)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 
scaleUp(int x)814     public int scaleUp(int x) {
815         return Region.clipRound(x * (double)windowScale);
816     }
817 
scaleDown(int x)818     public int scaleDown(int x) {
819         return Region.clipRound(x / (double)windowScale);
820     }
821 }
822