/* * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt.X11; import java.awt.Point; import java.awt.dnd.DnDConstants; import java.awt.event.MouseEvent; import java.io.IOException; import sun.misc.Unsafe; /** * XDropTargetProtocol implementation for Motif DnD protocol. * * @since 1.5 */ class MotifDnDDropTargetProtocol extends XDropTargetProtocol { private static final Unsafe unsafe = XlibWrapper.unsafe; private long sourceWindow = 0; private long sourceWindowMask = 0; private int sourceProtocolVersion = 0; private int sourceActions = DnDConstants.ACTION_NONE; private long[] sourceFormats = null; private long sourceAtom = 0; private int userAction = DnDConstants.ACTION_NONE; private int sourceX = 0; private int sourceY = 0; private XWindow targetXWindow = null; private boolean topLevelLeavePostponed = false; protected MotifDnDDropTargetProtocol(XDropTargetProtocolListener listener) { super(listener); } /** * Creates an instance associated with the specified listener. * * @throws NullPointerException if listener is null. */ static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) { return new MotifDnDDropTargetProtocol(listener); } public String getProtocolName() { return XDragAndDropProtocols.MotifDnD; } public void registerDropTarget(long window) { assert XToolkit.isAWTLockHeldByCurrentThread(); MotifDnDConstants.writeDragReceiverInfoStruct(window); } public void unregisterDropTarget(long window) { assert XToolkit.isAWTLockHeldByCurrentThread(); MotifDnDConstants.XA_MOTIF_ATOM_0.DeleteProperty(window); } public void registerEmbedderDropSite(long embedder) { assert XToolkit.isAWTLockHeldByCurrentThread(); boolean overriden = false; int version = 0; long proxy = 0; long newProxy = XDropTargetRegistry.getDnDProxyWindow(); int status = 0; long data = 0; int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE; WindowPropertyGetter wpg = new WindowPropertyGetter(embedder, MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF, false, XConstants.AnyPropertyType); try { status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); /* * DragICCI.h: * * typedef struct _xmDragReceiverInfoStruct{ * BYTE byte_order; * BYTE protocol_version; * BYTE drag_protocol_style; * BYTE pad1; * CARD32 proxy_window B32; * CARD16 num_drop_sites B16; * CARD16 pad2 B16; * CARD32 heap_offset B32; * } xmDragReceiverInfoStruct; */ if (status == (int)XConstants.Success && wpg.getData() != 0 && wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && wpg.getNumberOfItems() >= MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { overriden = true; data = wpg.getData(); dataSize = wpg.getNumberOfItems(); byte byteOrderByte = unsafe.getByte(data); { int tproxy = unsafe.getInt(data + 4); if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) { tproxy = MotifDnDConstants.Swapper.swap(tproxy); } proxy = tproxy; } if (proxy == newProxy) { // Embedder already registered. return; } { int tproxy = (int)newProxy; if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) { tproxy = MotifDnDConstants.Swapper.swap(tproxy); } unsafe.putInt(data + 4, tproxy); } } else { data = unsafe.allocateMemory(dataSize); unsafe.putByte(data, MotifDnDConstants.getByteOrderByte()); /* byte order */ unsafe.putByte(data + 1, MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION); /* protocol version */ unsafe.putByte(data + 2, (byte)MotifDnDConstants.MOTIF_DYNAMIC_STYLE); /* protocol style */ unsafe.putByte(data + 3, (byte)0); /* pad */ unsafe.putInt(data + 4, (int)newProxy); /* proxy window */ unsafe.putShort(data + 8, (short)0); /* num_drop_sites */ unsafe.putShort(data + 10, (short)0); /* pad */ unsafe.putInt(data + 12, dataSize); } XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder, MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), 8, XConstants.PropModeReplace, data, dataSize); XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); if ((XErrorHandlerUtil.saved_error != null) && (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { throw new XException("Cannot write Motif receiver info property"); } } finally { if (!overriden) { unsafe.freeMemory(data); data = 0; } wpg.dispose(); } putEmbedderRegistryEntry(embedder, overriden, version, proxy); } public void unregisterEmbedderDropSite(long embedder) { assert XToolkit.isAWTLockHeldByCurrentThread(); EmbedderRegistryEntry entry = getEmbedderRegistryEntry(embedder); if (entry == null) { return; } if (entry.isOverriden()) { int status = 0; WindowPropertyGetter wpg = new WindowPropertyGetter(embedder, MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF, false, XConstants.AnyPropertyType); try { status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); /* * DragICCI.h: * * typedef struct _xmDragReceiverInfoStruct{ * BYTE byte_order; * BYTE protocol_version; * BYTE drag_protocol_style; * BYTE pad1; * CARD32 proxy_window B32; * CARD16 num_drop_sites B16; * CARD16 pad2 B16; * CARD32 heap_offset B32; * } xmDragReceiverInfoStruct; */ if (status == (int)XConstants.Success && wpg.getData() != 0 && wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && wpg.getNumberOfItems() >= MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE; long data = wpg.getData(); byte byteOrderByte = unsafe.getByte(data); int tproxy = (int)entry.getProxy(); if (MotifDnDConstants.getByteOrderByte() != byteOrderByte) { tproxy = MotifDnDConstants.Swapper.swap(tproxy); } unsafe.putInt(data + 4, tproxy); XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); XlibWrapper.XChangeProperty(XToolkit.getDisplay(), embedder, MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(), 8, XConstants.PropModeReplace, data, dataSize); XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); if ((XErrorHandlerUtil.saved_error != null) && (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { throw new XException("Cannot write Motif receiver info property"); } } } finally { wpg.dispose(); } } else { MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO.DeleteProperty(embedder); } } /* * Gets and stores in the registry the embedder's Motif DnD drop site info * from the embedded. */ public void registerEmbeddedDropSite(long embedded) { assert XToolkit.isAWTLockHeldByCurrentThread(); boolean overriden = false; int version = 0; long proxy = 0; int status = 0; WindowPropertyGetter wpg = new WindowPropertyGetter(embedded, MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF, false, XConstants.AnyPropertyType); try { status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); /* * DragICCI.h: * * typedef struct _xmDragReceiverInfoStruct{ * BYTE byte_order; * BYTE protocol_version; * BYTE drag_protocol_style; * BYTE pad1; * CARD32 proxy_window B32; * CARD16 num_drop_sites B16; * CARD16 pad2 B16; * CARD32 heap_offset B32; * } xmDragReceiverInfoStruct; */ if (status == (int)XConstants.Success && wpg.getData() != 0 && wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && wpg.getNumberOfItems() >= MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { overriden = true; long data = wpg.getData(); byte byteOrderByte = unsafe.getByte(data); { int tproxy = unsafe.getInt(data + 4); if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) { tproxy = MotifDnDConstants.Swapper.swap(tproxy); } proxy = tproxy; } } } finally { wpg.dispose(); } putEmbedderRegistryEntry(embedded, overriden, version, proxy); } public boolean isProtocolSupported(long window) { WindowPropertyGetter wpg = new WindowPropertyGetter(window, MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF, false, XConstants.AnyPropertyType); try { int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); if (status == (int)XConstants.Success && wpg.getData() != 0 && wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && wpg.getNumberOfItems() >= MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { return true; } else { return false; } } finally { wpg.dispose(); } } private boolean processTopLevelEnter(XClientMessageEvent xclient) { assert XToolkit.isAWTLockHeldByCurrentThread(); if (targetXWindow != null || sourceWindow != 0) { return false; } if (!(XToolkit.windowToXWindow(xclient.get_window()) instanceof XWindow) && getEmbedderRegistryEntry(xclient.get_window()) == null) { return false; } long source_win = 0; long source_win_mask = 0; int protocol_version = 0; long property_atom = 0; long[] formats = null; { long data = xclient.get_data(); byte eventByteOrder = unsafe.getByte(data + 1); source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder); property_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); } /* Extract the available data types. */ { WindowPropertyGetter wpg = new WindowPropertyGetter(source_win, XAtom.get(property_atom), 0, 0xFFFF, false, MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom()); try { int status = wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); if (status == XConstants.Success && wpg.getData() != 0 && wpg.getActualType() == MotifDnDConstants.XA_MOTIF_DRAG_INITIATOR_INFO.getAtom() && wpg.getActualFormat() == 8 && wpg.getNumberOfItems() == MotifDnDConstants.MOTIF_INITIATOR_INFO_SIZE) { long data = wpg.getData(); byte propertyByteOrder = unsafe.getByte(data); protocol_version = unsafe.getByte(data + 1); if (protocol_version != MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION) { return false; } int index = MotifDnDConstants.Swapper.getShort(data + 2, propertyByteOrder); formats = MotifDnDConstants.getTargetListForIndex(index); } else { formats = new long[0]; } } finally { wpg.dispose(); } } /* * Select for StructureNotifyMask to receive DestroyNotify in case of source * crash. */ XWindowAttributes wattr = new XWindowAttributes(); try { XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), source_win, wattr.pData); XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); if ((status == 0) || ((XErrorHandlerUtil.saved_error != null) && (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) { throw new XException("XGetWindowAttributes failed"); } source_win_mask = wattr.get_your_event_mask(); } finally { wattr.dispose(); } XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); XlibWrapper.XSelectInput(XToolkit.getDisplay(), source_win, source_win_mask | XConstants.StructureNotifyMask); XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); if ((XErrorHandlerUtil.saved_error != null) && (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { throw new XException("XSelectInput failed"); } sourceWindow = source_win; sourceWindowMask = source_win_mask; sourceProtocolVersion = protocol_version; /* * TOP_LEVEL_ENTER doesn't communicate the list of supported actions * They are provided in DRAG_MOTION. */ sourceActions = DnDConstants.ACTION_NONE; sourceFormats = formats; sourceAtom = property_atom; return true; } private boolean processDragMotion(XClientMessageEvent xclient) { long data = xclient.get_data(); byte eventByteOrder = unsafe.getByte(data + 1); byte eventReason = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); int x = 0; int y = 0; short flags = MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder); int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >> MotifDnDConstants.MOTIF_DND_ACTION_SHIFT; int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >> MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT; int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action); int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions); /* Append source window id to the event data, so that we can send the response properly. */ { int win = (int)sourceWindow; if (eventByteOrder != MotifDnDConstants.getByteOrderByte()) { win = MotifDnDConstants.Swapper.swap(win); } unsafe.putInt(data + 12, win); } XWindow xwindow = null; { XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window()); if (xbasewindow instanceof XWindow) { xwindow = (XWindow)xbasewindow; } } if (eventReason == MotifDnDConstants.OPERATION_CHANGED) { /* OPERATION_CHANGED event doesn't provide coordinates, so we use previously stored position and component ref. */ x = sourceX; y = sourceY; if (xwindow == null) { xwindow = targetXWindow; } } else { x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder); y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder); if (xwindow == null) { long receiver = XDropTargetRegistry.getRegistry().getEmbeddedDropSite( xclient.get_window(), x, y); if (receiver != 0) { XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver); if (xbasewindow instanceof XWindow) { xwindow = (XWindow)xbasewindow; } } } if (xwindow != null) { Point p = xwindow.toLocal(x, y); x = p.x; y = p.y; } } if (xwindow == null) { if (targetXWindow != null) { notifyProtocolListener(targetXWindow, x, y, DnDConstants.ACTION_NONE, java_actions, xclient, MouseEvent.MOUSE_EXITED); } } else { int java_event_id = 0; if (targetXWindow == null) { java_event_id = MouseEvent.MOUSE_ENTERED; } else { java_event_id = MouseEvent.MOUSE_DRAGGED; } notifyProtocolListener(xwindow, x, y, java_action, java_actions, xclient, java_event_id); } sourceActions = java_actions; userAction = java_action; sourceX = x; sourceY = y; targetXWindow = xwindow; return true; } private boolean processTopLevelLeave(XClientMessageEvent xclient) { assert XToolkit.isAWTLockHeldByCurrentThread(); long data = xclient.get_data(); byte eventByteOrder = unsafe.getByte(data + 1); long source_win = MotifDnDConstants.Swapper.getInt(data + 8, eventByteOrder); /* Ignore Motif DnD messages from all other windows. */ if (source_win != sourceWindow) { return false; } /* * Postpone upcall to java, so that we can abort it in case * if drop immediatelly follows (see BugTraq ID 4395290). * Send a dummy ClientMessage event to guarantee that a postponed java * upcall will be processed. */ topLevelLeavePostponed = true; { long proxy; /* * If this is an embedded drop site, the event should go to the * awt_root_window as this is a proxy for all embedded drop sites. * Otherwise the event should go to the event->window, as we don't use * proxies for normal drop sites. */ if (getEmbedderRegistryEntry(xclient.get_window()) != null) { proxy = XDropTargetRegistry.getDnDProxyWindow(); } else { proxy = xclient.get_window(); } XClientMessageEvent dummy = new XClientMessageEvent(); try { dummy.set_type(XConstants.ClientMessage); dummy.set_window(xclient.get_window()); dummy.set_format(32); dummy.set_message_type(0); dummy.set_data(0, 0); dummy.set_data(1, 0); dummy.set_data(2, 0); dummy.set_data(3, 0); dummy.set_data(4, 0); XlibWrapper.XSendEvent(XToolkit.getDisplay(), proxy, false, XConstants.NoEventMask, dummy.pData); } finally { dummy.dispose(); } } return true; } private boolean processDropStart(XClientMessageEvent xclient) { long data = xclient.get_data(); byte eventByteOrder = unsafe.getByte(data + 1); long source_win = MotifDnDConstants.Swapper.getInt(data + 16, eventByteOrder); /* Ignore Motif DnD messages from all other windows. */ if (source_win != sourceWindow) { return false; } long property_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); short flags = MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder); int motif_action = (flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >> MotifDnDConstants.MOTIF_DND_ACTION_SHIFT; int motif_actions = (flags & MotifDnDConstants.MOTIF_DND_ACTIONS_MASK) >> MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT; int java_action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action); int java_actions = MotifDnDConstants.getJavaActionsForMotifActions(motif_actions); int x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder); int y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder); XWindow xwindow = null; { XBaseWindow xbasewindow = XToolkit.windowToXWindow(xclient.get_window()); if (xbasewindow instanceof XWindow) { xwindow = (XWindow)xbasewindow; } } if (xwindow == null) { long receiver = XDropTargetRegistry.getRegistry().getEmbeddedDropSite( xclient.get_window(), x, y); if (receiver != 0) { XBaseWindow xbasewindow = XToolkit.windowToXWindow(receiver); if (xbasewindow instanceof XWindow) { xwindow = (XWindow)xbasewindow; } } } if (xwindow != null) { Point p = xwindow.toLocal(x, y); x = p.x; y = p.y; } if (xwindow != null) { notifyProtocolListener(xwindow, x, y, java_action, java_actions, xclient, MouseEvent.MOUSE_RELEASED); } else if (targetXWindow != null) { notifyProtocolListener(targetXWindow, x, y, DnDConstants.ACTION_NONE, java_actions, xclient, MouseEvent.MOUSE_EXITED); } return true; } public int getMessageType(XClientMessageEvent xclient) { if (xclient.get_message_type() != MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { return UNKNOWN_MESSAGE; } long data = xclient.get_data(); byte reason = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); switch (reason) { case MotifDnDConstants.TOP_LEVEL_ENTER : return ENTER_MESSAGE; case MotifDnDConstants.DRAG_MOTION : case MotifDnDConstants.OPERATION_CHANGED : return MOTION_MESSAGE; case MotifDnDConstants.TOP_LEVEL_LEAVE : return LEAVE_MESSAGE; case MotifDnDConstants.DROP_START : return DROP_MESSAGE; default: return UNKNOWN_MESSAGE; } } protected boolean processClientMessageImpl(XClientMessageEvent xclient) { if (xclient.get_message_type() != MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { if (topLevelLeavePostponed) { topLevelLeavePostponed = false; cleanup(); } return false; } long data = xclient.get_data(); byte reason = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); byte origin = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); if (topLevelLeavePostponed) { topLevelLeavePostponed = false; if (reason != MotifDnDConstants.DROP_START) { cleanup(); } } /* Only initiator messages should be handled. */ if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { return false; } switch (reason) { case MotifDnDConstants.TOP_LEVEL_ENTER : return processTopLevelEnter(xclient); case MotifDnDConstants.DRAG_MOTION : case MotifDnDConstants.OPERATION_CHANGED : return processDragMotion(xclient); case MotifDnDConstants.TOP_LEVEL_LEAVE : return processTopLevelLeave(xclient); case MotifDnDConstants.DROP_START : return processDropStart(xclient); default: return false; } } /* * Currently we don't synthesize enter/leave messages for Motif DnD * protocol. See comments in XDropTargetProtocol.postProcessClientMessage. */ protected void sendEnterMessageToToplevel(long win, XClientMessageEvent xclient) { throw new Error("UNIMPLEMENTED"); } protected void sendLeaveMessageToToplevel(long win, XClientMessageEvent xclient) { throw new Error("UNIMPLEMENTED"); } public boolean forwardEventToEmbedded(long embedded, long ctxt, int eventID) { // UNIMPLEMENTED. return false; } public boolean isXEmbedSupported() { return false; } public boolean sendResponse(long ctxt, int eventID, int action) { XClientMessageEvent xclient = new XClientMessageEvent(ctxt); if (xclient.get_message_type() != MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { return false; } long data = xclient.get_data(); byte reason = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); byte origin = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); byte eventByteOrder = unsafe.getByte(data + 1); byte response_reason = (byte)0; /* Only initiator messages should be handled. */ if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { return false; } switch (reason) { case MotifDnDConstants.TOP_LEVEL_ENTER: case MotifDnDConstants.TOP_LEVEL_LEAVE: /* Receiver shouldn't rely to these messages. */ return false; case MotifDnDConstants.DRAG_MOTION: switch (eventID) { case MouseEvent.MOUSE_ENTERED: response_reason = MotifDnDConstants.DROP_SITE_ENTER; break; case MouseEvent.MOUSE_DRAGGED: response_reason = MotifDnDConstants.DRAG_MOTION; break; case MouseEvent.MOUSE_EXITED: response_reason = MotifDnDConstants.DROP_SITE_LEAVE; break; } break; case MotifDnDConstants.OPERATION_CHANGED: case MotifDnDConstants.DROP_START: response_reason = reason; break; default: // Unknown reason. Shouldn't get here. assert false; } XClientMessageEvent msg = new XClientMessageEvent(); try { msg.set_type(XConstants.ClientMessage); msg.set_window(MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder)); msg.set_format(8); msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()); long responseData = msg.get_data(); unsafe.putByte(responseData, (byte)(response_reason | MotifDnDConstants.MOTIF_MESSAGE_FROM_RECEIVER)); unsafe.putByte(responseData + 1, MotifDnDConstants.getByteOrderByte()); int response_flags = 0; if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) { short flags = MotifDnDConstants.Swapper.getShort(data + 2, eventByteOrder); byte dropSiteStatus = (action == DnDConstants.ACTION_NONE) ? MotifDnDConstants.MOTIF_INVALID_DROP_SITE : MotifDnDConstants.MOTIF_VALID_DROP_SITE; /* Clear action and drop site status bits. */ response_flags = flags & ~MotifDnDConstants.MOTIF_DND_ACTION_MASK & ~MotifDnDConstants.MOTIF_DND_STATUS_MASK; /* Fill in new action and drop site status. */ response_flags |= MotifDnDConstants.getMotifActionsForJavaActions(action) << MotifDnDConstants.MOTIF_DND_ACTION_SHIFT; response_flags |= dropSiteStatus << MotifDnDConstants.MOTIF_DND_STATUS_SHIFT; } else { response_flags = 0; } unsafe.putShort(responseData + 2, (short)response_flags); /* Write time stamp. */ int time = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder); unsafe.putInt(responseData + 4, time); /* Write coordinates. */ if (response_reason != MotifDnDConstants.DROP_SITE_LEAVE) { short x = MotifDnDConstants.Swapper.getShort(data + 8, eventByteOrder); short y = MotifDnDConstants.Swapper.getShort(data + 10, eventByteOrder); unsafe.putShort(responseData + 8, x); // x unsafe.putShort(responseData + 10, y); // y } else { unsafe.putShort(responseData + 8, (short)0); // x unsafe.putShort(responseData + 10, (short)0); // y } XToolkit.awtLock(); try { XlibWrapper.XSendEvent(XToolkit.getDisplay(), msg.get_window(), false, XConstants.NoEventMask, msg.pData); } finally { XToolkit.awtUnlock(); } } finally { msg.dispose(); } return true; } public Object getData(long ctxt, long format) throws IllegalArgumentException, IOException { XClientMessageEvent xclient = new XClientMessageEvent(ctxt); if (xclient.get_message_type() != MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { throw new IllegalArgumentException(); } long data = xclient.get_data(); byte reason = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); byte origin = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); byte eventByteOrder = unsafe.getByte(data + 1); if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { throw new IOException("Cannot get data: corrupted context"); } long selatom = 0; switch (reason) { case MotifDnDConstants.DRAG_MOTION : case MotifDnDConstants.OPERATION_CHANGED : selatom = sourceAtom; break; case MotifDnDConstants.DROP_START : selatom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); break; default: throw new IOException("Cannot get data: invalid message reason"); } if (selatom == 0) { throw new IOException("Cannot get data: drag source property atom unavailable"); } long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL; // with correction of (32-bit unsigned to 64-bit signed) implicit conversion. XAtom selectionAtom = XAtom.get(selatom); XSelection selection = XSelection.getSelection(selectionAtom); if (selection == null) { selection = new XSelection(selectionAtom); } return selection.getData(format, time_stamp); } public boolean sendDropDone(long ctxt, boolean success, int dropAction) { XClientMessageEvent xclient = new XClientMessageEvent(ctxt); if (xclient.get_message_type() != MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom()) { return false; } long data = xclient.get_data(); byte reason = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_REASON_MASK); byte origin = (byte)(unsafe.getByte(data) & MotifDnDConstants.MOTIF_MESSAGE_SENDER_MASK); byte eventByteOrder = unsafe.getByte(data + 1); if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR) { return false; } if (reason != MotifDnDConstants.DROP_START) { return false; } long time_stamp = MotifDnDConstants.Swapper.getInt(data + 4, eventByteOrder) & 0xffffffffL; // with correction of (32-bit unsigned to 64-bit signed) implicit conversion. long sel_atom = MotifDnDConstants.Swapper.getInt(data + 12, eventByteOrder); long status_atom = 0; if (success) { status_atom = MotifDnDConstants.XA_XmTRANSFER_SUCCESS.getAtom(); } else { status_atom = MotifDnDConstants.XA_XmTRANSFER_FAILURE.getAtom(); } XToolkit.awtLock(); try { XlibWrapper.XConvertSelection(XToolkit.getDisplay(), sel_atom, status_atom, MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom(), XWindow.getXAWTRootWindow().getWindow(), time_stamp); /* * Flush the buffer to guarantee that the drop completion event is sent * to the source before the method returns. */ XlibWrapper.XFlush(XToolkit.getDisplay()); } finally { XToolkit.awtUnlock(); } /* Trick to prevent cleanup() from posting dragExit */ targetXWindow = null; /* Cannot do cleanup before the drop finishes as we may need source protocol version to send drop finished message. */ cleanup(); return true; } public final long getSourceWindow() { return sourceWindow; } /** * Reset the state of the object. */ public void cleanup() { // Clear the reference to this protocol. XDropTargetEventProcessor.reset(); if (targetXWindow != null) { notifyProtocolListener(targetXWindow, 0, 0, DnDConstants.ACTION_NONE, sourceActions, null, MouseEvent.MOUSE_EXITED); } if (sourceWindow != 0) { XToolkit.awtLock(); try { XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); XlibWrapper.XSelectInput(XToolkit.getDisplay(), sourceWindow, sourceWindowMask); XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); } finally { XToolkit.awtUnlock(); } } sourceWindow = 0; sourceWindowMask = 0; sourceProtocolVersion = 0; sourceActions = DnDConstants.ACTION_NONE; sourceFormats = null; sourceAtom = 0; userAction = DnDConstants.ACTION_NONE; sourceX = 0; sourceY = 0; targetXWindow = null; topLevelLeavePostponed = false; } public boolean isDragOverComponent() { return targetXWindow != null; } private void notifyProtocolListener(XWindow xwindow, int x, int y, int dropAction, int actions, XClientMessageEvent xclient, int eventID) { long nativeCtxt = 0; // Make a copy of the passed XClientMessageEvent structure, since // the original structure can be freed before this // SunDropTargetEvent is dispatched. if (xclient != null) { int size = XClientMessageEvent.getSize(); nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize()); unsafe.copyMemory(xclient.pData, nativeCtxt, size); } getProtocolListener().handleDropTargetNotification(xwindow, x, y, dropAction, actions, sourceFormats, nativeCtxt, eventID); } }