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