Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/awt/X11/XDragSourceContextPeer.java
32288 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt.X11;2627import java.awt.Component;28import java.awt.Cursor;29import java.awt.Window;3031import java.awt.datatransfer.Transferable;3233import java.awt.dnd.DnDConstants;34import java.awt.dnd.DragGestureEvent;35import java.awt.dnd.InvalidDnDOperationException;3637import java.util.*;3839import sun.util.logging.PlatformLogger;4041import sun.awt.dnd.SunDragSourceContextPeer;42import sun.awt.dnd.SunDropTargetContextPeer;43import sun.awt.SunToolkit;44import sun.awt.AWTAccessor;4546/**47* The XDragSourceContextPeer class is the class responsible for handling48* the interaction between the XDnD/Motif DnD subsystem and Java drag sources.49*50* @since 1.551*/52public final class XDragSourceContextPeer53extends SunDragSourceContextPeer implements XDragSourceProtocolListener {54private static final PlatformLogger logger =55PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDragSourceContextPeer");5657/* The events selected on the root window when the drag begins. */58private static final int ROOT_EVENT_MASK = (int)XConstants.ButtonMotionMask |59(int)XConstants.KeyPressMask | (int)XConstants.KeyReleaseMask;60/* The events to be delivered during grab. */61private static final int GRAB_EVENT_MASK = (int)XConstants.ButtonPressMask |62(int)XConstants.ButtonMotionMask | (int)XConstants.ButtonReleaseMask;6364/* The event mask of the root window before the drag operation starts. */65private long rootEventMask = 0;66private boolean dndInProgress = false;67private boolean dragInProgress = false;68private long dragRootWindow = 0;6970/* The protocol chosen for the communication with the current drop target. */71private XDragSourceProtocol dragProtocol = null;72/* The drop action chosen by the current drop target. */73private int targetAction = DnDConstants.ACTION_NONE;74/* The set of drop actions supported by the drag source. */75private int sourceActions = DnDConstants.ACTION_NONE;76/* The drop action selected by the drag source based on the modifiers state77and the action selected by the current drop target. */78private int sourceAction = DnDConstants.ACTION_NONE;79/* The data formats supported by the drag source for the current drag80operation. */81private long[] sourceFormats = null;82/* The XID of the root subwindow that contains the current target. */83private long targetRootSubwindow = 0;84/* The pointer location. */85private int xRoot = 0;86private int yRoot = 0;87/* Keyboard modifiers state. */88private int eventState = 0;8990/* XEmbed DnD support. We act as a proxy between source and target. */91private long proxyModeSourceWindow = 0;9293/* The singleton instance. */94private static final XDragSourceContextPeer theInstance =95new XDragSourceContextPeer(null);9697private XDragSourceContextPeer(DragGestureEvent dge) {98super(dge);99}100101static XDragSourceProtocolListener getXDragSourceProtocolListener() {102return theInstance;103}104105static XDragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge)106throws InvalidDnDOperationException {107theInstance.setTrigger(dge);108return theInstance;109}110111protected void startDrag(Transferable transferable,112long[] formats, Map formatMap) {113Component component = getTrigger().getComponent();114Component c = null;115XWindowPeer wpeer = null;116117for (c = component; c != null && !(c instanceof Window);118c = AWTAccessor.getComponentAccessor().getParent(c));119120if (c instanceof Window) {121wpeer = (XWindowPeer)c.getPeer();122}123124if (wpeer == null) {125throw new InvalidDnDOperationException(126"Cannot find top-level for the drag source component");127}128129long xcursor = 0;130long rootWindow = 0;131long dragWindow = 0;132long timeStamp = 0;133134/* Retrieve the X cursor for the drag operation. */135{136Cursor cursor = getCursor();137if (cursor != null) {138xcursor = XGlobalCursorManager.getCursor(cursor);139}140}141142XToolkit.awtLock();143try {144if (proxyModeSourceWindow != 0) {145throw new InvalidDnDOperationException("Proxy drag in progress");146}147if (dndInProgress) {148throw new InvalidDnDOperationException("Drag in progress");149}150151/* Determine the root window for the drag operation. */152{153long screen = XlibWrapper.XScreenNumberOfScreen(wpeer.getScreen());154rootWindow = XlibWrapper.RootWindow(XToolkit.getDisplay(), screen);155}156157dragWindow = XWindow.getXAWTRootWindow().getWindow();158159timeStamp = XToolkit.getCurrentServerTime();160161int dropActions = getDragSourceContext().getSourceActions();162163Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols();164while (dragProtocols.hasNext()) {165XDragSourceProtocol dragProtocol = (XDragSourceProtocol)dragProtocols.next();166try {167dragProtocol.initializeDrag(dropActions, transferable,168formatMap, formats);169} catch (XException xe) {170throw (InvalidDnDOperationException)171new InvalidDnDOperationException().initCause(xe);172}173}174175/* Install X grabs. */176{177int status;178XWindowAttributes wattr = new XWindowAttributes();179try {180status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),181rootWindow, wattr.pData);182183if (status == 0) {184throw new InvalidDnDOperationException("XGetWindowAttributes failed");185}186187rootEventMask = wattr.get_your_event_mask();188189XlibWrapper.XSelectInput(XToolkit.getDisplay(), rootWindow,190rootEventMask | ROOT_EVENT_MASK);191} finally {192wattr.dispose();193}194195XBaseWindow.ungrabInput();196197status = XlibWrapper.XGrabPointer(XToolkit.getDisplay(), rootWindow,1980, GRAB_EVENT_MASK,199XConstants.GrabModeAsync,200XConstants.GrabModeAsync,201XConstants.None, xcursor, timeStamp);202203if (status != XConstants.GrabSuccess) {204cleanup(timeStamp);205throwGrabFailureException("Cannot grab pointer", status);206return;207}208209status = XlibWrapper.XGrabKeyboard(XToolkit.getDisplay(), rootWindow,2100,211XConstants.GrabModeAsync,212XConstants.GrabModeAsync,213timeStamp);214215if (status != XConstants.GrabSuccess) {216cleanup(timeStamp);217throwGrabFailureException("Cannot grab keyboard", status);218return;219}220}221222/* Update the global state. */223dndInProgress = true;224dragInProgress = true;225dragRootWindow = rootWindow;226sourceActions = dropActions;227sourceFormats = formats;228} finally {229XToolkit.awtUnlock();230}231232/* This implementation doesn't use native context */233setNativeContext(0);234235SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(transferable);236}237238public long getProxyModeSourceWindow() {239return proxyModeSourceWindow;240}241242private void setProxyModeSourceWindowImpl(long window) {243proxyModeSourceWindow = window;244}245246public static void setProxyModeSourceWindow(long window) {247theInstance.setProxyModeSourceWindowImpl(window);248}249250/**251* set cursor252*/253254public void setCursor(Cursor c) throws InvalidDnDOperationException {255XToolkit.awtLock();256try {257super.setCursor(c);258} finally {259XToolkit.awtUnlock();260}261}262263protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) {264assert XToolkit.isAWTLockHeldByCurrentThread();265266if (c == null) {267return;268}269270long xcursor = XGlobalCursorManager.getCursor(c);271272if (xcursor == 0) {273return;274}275276XlibWrapper.XChangeActivePointerGrab(XToolkit.getDisplay(),277GRAB_EVENT_MASK,278xcursor,279XConstants.CurrentTime);280}281282protected boolean needsBogusExitBeforeDrop() {283return false;284}285286private void throwGrabFailureException(String msg, int grabStatus)287throws InvalidDnDOperationException {288String msgCause = "";289switch (grabStatus) {290case XConstants.GrabNotViewable: msgCause = "not viewable"; break;291case XConstants.AlreadyGrabbed: msgCause = "already grabbed"; break;292case XConstants.GrabInvalidTime: msgCause = "invalid time"; break;293case XConstants.GrabFrozen: msgCause = "grab frozen"; break;294default: msgCause = "unknown failure"; break;295}296throw new InvalidDnDOperationException(msg + ": " + msgCause);297}298299/**300* The caller must own awtLock.301*/302public void cleanup(long time) {303if (dndInProgress) {304if (dragProtocol != null) {305dragProtocol.sendLeaveMessage(time);306}307308if (targetAction != DnDConstants.ACTION_NONE) {309dragExit(xRoot, yRoot);310}311312dragDropFinished(false, DnDConstants.ACTION_NONE, xRoot, yRoot);313}314315Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols();316while (dragProtocols.hasNext()) {317XDragSourceProtocol dragProtocol = (XDragSourceProtocol)dragProtocols.next();318try {319dragProtocol.cleanup();320} catch (XException xe) {321// Ignore the exception.322}323}324325dndInProgress = false;326dragInProgress = false;327dragRootWindow = 0;328sourceFormats = null;329sourceActions = DnDConstants.ACTION_NONE;330sourceAction = DnDConstants.ACTION_NONE;331eventState = 0;332xRoot = 0;333yRoot = 0;334335cleanupTargetInfo();336337removeDnDGrab(time);338}339340/**341* The caller must own awtLock.342*/343private void cleanupTargetInfo() {344targetAction = DnDConstants.ACTION_NONE;345dragProtocol = null;346targetRootSubwindow = 0;347}348349private void removeDnDGrab(long time) {350assert XToolkit.isAWTLockHeldByCurrentThread();351352XlibWrapper.XUngrabPointer(XToolkit.getDisplay(), time);353XlibWrapper.XUngrabKeyboard(XToolkit.getDisplay(), time);354355/* Restore the root event mask if it was changed. */356if ((rootEventMask | ROOT_EVENT_MASK) != rootEventMask &&357dragRootWindow != 0) {358359XlibWrapper.XSelectInput(XToolkit.getDisplay(),360dragRootWindow,361rootEventMask);362}363364rootEventMask = 0;365dragRootWindow = 0;366}367368private boolean processClientMessage(XClientMessageEvent xclient) {369if (dragProtocol != null) {370return dragProtocol.processClientMessage(xclient);371}372return false;373}374375/**376* Updates the source action according to the specified state.377*378* @returns true if the source379*/380private boolean updateSourceAction(int state) {381int action = SunDragSourceContextPeer.convertModifiersToDropAction(XWindow.getModifiers(state, 0, 0),382sourceActions);383if (sourceAction == action) {384return false;385}386sourceAction = action;387return true;388}389390/**391* Returns the client window under the specified root subwindow.392*/393private static long findClientWindow(long window) {394if (XlibUtil.isTrueToplevelWindow(window)) {395return window;396}397398Set<Long> children = XlibUtil.getChildWindows(window);399for (Long child : children) {400long win = findClientWindow(child);401if (win != 0) {402return win;403}404}405406return 0;407}408409private void doUpdateTargetWindow(long subwindow, long time) {410long clientWindow = 0;411long proxyWindow = 0;412XDragSourceProtocol protocol = null;413boolean isReceiver = false;414415if (subwindow != 0) {416clientWindow = findClientWindow(subwindow);417}418419if (clientWindow != 0) {420Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols();421while (dragProtocols.hasNext()) {422XDragSourceProtocol dragProtocol = (XDragSourceProtocol)dragProtocols.next();423if (dragProtocol.attachTargetWindow(clientWindow, time)) {424protocol = dragProtocol;425break;426}427}428}429430/* Update the global state. */431dragProtocol = protocol;432targetAction = DnDConstants.ACTION_NONE;433targetRootSubwindow = subwindow;434}435436private void updateTargetWindow(XMotionEvent xmotion) {437assert XToolkit.isAWTLockHeldByCurrentThread();438439int x = xmotion.get_x_root();440int y = xmotion.get_y_root();441long time = xmotion.get_time();442long subwindow = xmotion.get_subwindow();443444/*445* If this event had occurred before the pointer was grabbed,446* query the server for the current root subwindow.447*/448if (xmotion.get_window() != xmotion.get_root()) {449XlibWrapper.XQueryPointer(XToolkit.getDisplay(),450xmotion.get_root(),451XlibWrapper.larg1, // root452XlibWrapper.larg2, // subwindow453XlibWrapper.larg3, // x_root454XlibWrapper.larg4, // y_root455XlibWrapper.larg5, // x456XlibWrapper.larg6, // y457XlibWrapper.larg7); // modifiers458subwindow = Native.getLong(XlibWrapper.larg2);459}460461if (targetRootSubwindow != subwindow) {462if (dragProtocol != null) {463dragProtocol.sendLeaveMessage(time);464465/*466* Neither Motif DnD nor XDnD provide a mean for the target467* to notify the source that the pointer exits the drop site468* that occupies the whole top level.469* We detect this situation and post dragExit.470*/471if (targetAction != DnDConstants.ACTION_NONE) {472dragExit(x, y);473}474}475476/* Update the global state. */477doUpdateTargetWindow(subwindow, time);478479if (dragProtocol != null) {480dragProtocol.sendEnterMessage(sourceFormats,481sourceAction,482sourceActions,483time);484}485}486}487488/*489* DO NOT USE is_hint field of xmotion since it could not be set when we490* convert XKeyEvent or XButtonRelease to XMotionEvent.491*/492private void processMouseMove(XMotionEvent xmotion) {493if (!dragInProgress) {494return;495}496if (xRoot != xmotion.get_x_root() || yRoot != xmotion.get_y_root()) {497xRoot = xmotion.get_x_root();498yRoot = xmotion.get_y_root();499500postDragSourceDragEvent(targetAction,501XWindow.getModifiers(xmotion.get_state(),0,0),502xRoot, yRoot, DISPATCH_MOUSE_MOVED);503}504505if (eventState != xmotion.get_state()) {506if (updateSourceAction(xmotion.get_state()) && dragProtocol != null) {507postDragSourceDragEvent(targetAction,508XWindow.getModifiers(xmotion.get_state(),0,0),509xRoot, yRoot, DISPATCH_CHANGED);510}511eventState = xmotion.get_state();512}513514updateTargetWindow(xmotion);515516if (dragProtocol != null) {517dragProtocol.sendMoveMessage(xmotion.get_x_root(),518xmotion.get_y_root(),519sourceAction, sourceActions,520xmotion.get_time());521}522}523524private void processDrop(XButtonEvent xbutton) {525try {526dragProtocol.initiateDrop(xbutton.get_x_root(),527xbutton.get_y_root(),528sourceAction, sourceActions,529xbutton.get_time());530} catch (XException e) {531cleanup(xbutton.get_time());532}533}534535private boolean processProxyModeEvent(XEvent ev) {536if (getProxyModeSourceWindow() == 0) {537return false;538}539540if (ev.get_type() != (int)XConstants.ClientMessage) {541return false;542}543544if (logger.isLoggable(PlatformLogger.Level.FINEST)) {545logger.finest(" proxyModeSourceWindow=" +546getProxyModeSourceWindow() +547" ev=" + ev);548}549550XClientMessageEvent xclient = ev.get_xclient();551552Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols();553while (dragProtocols.hasNext()) {554XDragSourceProtocol dragProtocol =555(XDragSourceProtocol)dragProtocols.next();556if (dragProtocol.processProxyModeEvent(xclient,557getProxyModeSourceWindow())) {558return true;559}560}561562return false;563}564565/**566* The caller must own awtLock.567*568* @returns true if the even was processed and shouldn't be passed along.569*/570private boolean doProcessEvent(XEvent ev) {571assert XToolkit.isAWTLockHeldByCurrentThread();572573if (processProxyModeEvent(ev)) {574return true;575}576577if (!dndInProgress) {578return false;579}580581switch (ev.get_type()) {582case XConstants.ClientMessage: {583XClientMessageEvent xclient = ev.get_xclient();584return processClientMessage(xclient);585}586case XConstants.DestroyNotify: {587XDestroyWindowEvent xde = ev.get_xdestroywindow();588589/* Target crashed during drop processing - cleanup. */590if (!dragInProgress &&591dragProtocol != null &&592xde.get_window() == dragProtocol.getTargetWindow()) {593cleanup(XConstants.CurrentTime);594return true;595}596/* Pass along */597return false;598}599}600601if (!dragInProgress) {602return false;603}604605/* Process drag-only messages. */606switch (ev.get_type()) {607case XConstants.KeyRelease:608case XConstants.KeyPress: {609XKeyEvent xkey = ev.get_xkey();610long keysym = XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(),611xkey.get_keycode(), 0);612switch ((int)keysym) {613case (int)XKeySymConstants.XK_Escape: {614if (ev.get_type() == (int)XConstants.KeyRelease) {615cleanup(xkey.get_time());616}617break;618}619case (int)XKeySymConstants.XK_Control_R:620case (int)XKeySymConstants.XK_Control_L:621case (int)XKeySymConstants.XK_Shift_R:622case (int)XKeySymConstants.XK_Shift_L: {623XlibWrapper.XQueryPointer(XToolkit.getDisplay(),624xkey.get_root(),625XlibWrapper.larg1, // root626XlibWrapper.larg2, // subwindow627XlibWrapper.larg3, // x_root628XlibWrapper.larg4, // y_root629XlibWrapper.larg5, // x630XlibWrapper.larg6, // y631XlibWrapper.larg7); // modifiers632XMotionEvent xmotion = new XMotionEvent();633try {634xmotion.set_type(XConstants.MotionNotify);635xmotion.set_serial(xkey.get_serial());636xmotion.set_send_event(xkey.get_send_event());637xmotion.set_display(xkey.get_display());638xmotion.set_window(xkey.get_window());639xmotion.set_root(xkey.get_root());640xmotion.set_subwindow(xkey.get_subwindow());641xmotion.set_time(xkey.get_time());642xmotion.set_x(xkey.get_x());643xmotion.set_y(xkey.get_y());644xmotion.set_x_root(xkey.get_x_root());645xmotion.set_y_root(xkey.get_y_root());646xmotion.set_state((int)Native.getLong(XlibWrapper.larg7));647// we do not use this field, so it's unset for now648// xmotion.set_is_hint(???);649xmotion.set_same_screen(xkey.get_same_screen());650651//It's safe to use key event as motion event since we use only their common fields.652processMouseMove(xmotion);653} finally {654xmotion.dispose();655}656break;657}658}659return true;660}661case XConstants.ButtonPress:662return true;663case XConstants.MotionNotify:664processMouseMove(ev.get_xmotion());665return true;666case XConstants.ButtonRelease: {667XButtonEvent xbutton = ev.get_xbutton();668/*669* Ignore the buttons above 20 due to the bit limit for670* InputEvent.BUTTON_DOWN_MASK.671* One more bit is reserved for FIRST_HIGH_BIT.672*/673if (xbutton.get_button() > SunToolkit.MAX_BUTTONS_SUPPORTED) {674return true;675}676677/*678* On some X servers it could happen that ButtonRelease coordinates679* differ from the latest MotionNotify coordinates, so we need to680* process it as a mouse motion.681*/682XMotionEvent xmotion = new XMotionEvent();683try {684xmotion.set_type(XConstants.MotionNotify);685xmotion.set_serial(xbutton.get_serial());686xmotion.set_send_event(xbutton.get_send_event());687xmotion.set_display(xbutton.get_display());688xmotion.set_window(xbutton.get_window());689xmotion.set_root(xbutton.get_root());690xmotion.set_subwindow(xbutton.get_subwindow());691xmotion.set_time(xbutton.get_time());692xmotion.set_x(xbutton.get_x());693xmotion.set_y(xbutton.get_y());694xmotion.set_x_root(xbutton.get_x_root());695xmotion.set_y_root(xbutton.get_y_root());696xmotion.set_state(xbutton.get_state());697// we do not use this field, so it's unset for now698// xmotion.set_is_hint(???);699xmotion.set_same_screen(xbutton.get_same_screen());700701//It's safe to use key event as motion event since we use only their common fields.702processMouseMove(xmotion);703} finally {704xmotion.dispose();705}706if (xbutton.get_button() == XConstants.buttons[0]707|| xbutton.get_button() == XConstants.buttons[1]) {708// drag is initiated with Button1 or Button2 pressed and709// ended on release of either of these buttons (as the same710// behavior was with our old Motif DnD-based implementation)711removeDnDGrab(xbutton.get_time());712dragInProgress = false;713if (dragProtocol != null && targetAction != DnDConstants.ACTION_NONE) {714/*715* ACTION_NONE indicates that either the drop target rejects the716* drop or it haven't responded yet. The latter could happen in717* case of fast drag, slow target-server connection or slow718* drag notifications processing on the target side.719*/720processDrop(xbutton);721} else {722cleanup(xbutton.get_time());723}724}725return true;726}727}728729return false;730}731732static boolean processEvent(XEvent ev) {733XToolkit.awtLock();734try {735try {736return theInstance.doProcessEvent(ev);737} catch (XException e) {738e.printStackTrace();739return false;740}741} finally {742XToolkit.awtUnlock();743}744}745746/* XDragSourceProtocolListener implementation */747748public void handleDragReply(int action) {749// NOTE: we have to use the current pointer location, since750// the target didn't specify the coordinates for the reply.751handleDragReply(action, xRoot, yRoot);752}753754public void handleDragReply(int action, int x, int y) {755// NOTE: we have to use the current modifiers state, since756// the target didn't specify the modifiers state for the reply.757handleDragReply(action, xRoot, yRoot, XWindow.getModifiers(eventState,0,0));758}759760public void handleDragReply(int action, int x, int y, int modifiers) {761if (action == DnDConstants.ACTION_NONE &&762targetAction != DnDConstants.ACTION_NONE) {763dragExit(x, y);764} else if (action != DnDConstants.ACTION_NONE) {765int type = 0;766767if (targetAction == DnDConstants.ACTION_NONE) {768type = SunDragSourceContextPeer.DISPATCH_ENTER;769} else {770type = SunDragSourceContextPeer.DISPATCH_MOTION;771}772773// Note that we use the modifiers state a774postDragSourceDragEvent(action, modifiers, x, y, type);775}776777targetAction = action;778}779780public void handleDragFinished() {781/* Assume that the drop was successful. */782handleDragFinished(true);783}784785public void handleDragFinished(boolean success) {786/* Assume that the performed drop action is the latest drop action787accepted by the drop target. */788handleDragFinished(true, targetAction);789}790791public void handleDragFinished(boolean success, int action) {792// NOTE: we have to use the current pointer location, since793// the target didn't specify the coordinates for the reply.794handleDragFinished(success, action, xRoot, yRoot);795}796797public void handleDragFinished(boolean success, int action, int x, int y) {798dragDropFinished(success, action, x, y);799800dndInProgress = false;801cleanup(XConstants.CurrentTime);802}803}804805806