| /* | |
| * Copyright (c) 2003, 2014, 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 jdk.internal.misc.Unsafe; | |
| /** | |
| * XDropTargetProtocol implementation for Motif DnD protocol. | |
| * | |
| * @since 1.5 | |
| */ | |
| final 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 {@code null}. | |
| */ | |
| static XDropTargetProtocol createInstance(XDropTargetProtocolListener listener) { | |
| return new MotifDnDDropTargetProtocol(listener); | |
| } | |
| @Override | |
| public String getProtocolName() { | |
| return XDragAndDropProtocols.MotifDnD; | |
| } | |
| @Override | |
| public void registerDropTarget(long window) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| MotifDnDConstants.writeDragReceiverInfoStruct(window); | |
| } | |
| @Override | |
| public void unregisterDropTarget(long window) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| MotifDnDConstants.XA_MOTIF_ATOM_0.DeleteProperty(window); | |
| } | |
| @Override | |
| public void registerEmbedderDropSite(long embedder) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| boolean overridden = 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 == XConstants.Success && wpg.getData() != 0 && | |
| wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && | |
| wpg.getNumberOfItems() >= | |
| MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { | |
| overridden = 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 (!overridden) { | |
| unsafe.freeMemory(data); | |
| data = 0; | |
| } | |
| wpg.dispose(); | |
| } | |
| putEmbedderRegistryEntry(embedder, overridden, version, proxy); | |
| } | |
| @Override | |
| 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 == 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. | |
| */ | |
| @Override | |
| public void registerEmbeddedDropSite(long embedded) { | |
| assert XToolkit.isAWTLockHeldByCurrentThread(); | |
| boolean overridden = 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 == XConstants.Success && wpg.getData() != 0 && | |
| wpg.getActualType() != 0 && wpg.getActualFormat() == 8 && | |
| wpg.getNumberOfItems() >= | |
| MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE) { | |
| overridden = 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, overridden, version, proxy); | |
| } | |
| @Override | |
| 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 == 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 immediately 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; | |
| } | |
| @Override | |
| 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; | |
| } | |
| } | |
| @Override | |
| 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. | |
| */ | |
| @Override | |
| protected void sendEnterMessageToToplevel(long win, | |
| XClientMessageEvent xclient) { | |
| throw new Error("UNIMPLEMENTED"); | |
| } | |
| @Override | |
| protected void sendLeaveMessageToToplevel(long win, | |
| XClientMessageEvent xclient) { | |
| throw new Error("UNIMPLEMENTED"); | |
| } | |
| @Override | |
| public boolean forwardEventToEmbedded(long embedded, long ctxt, | |
| int eventID) { | |
| // UNIMPLEMENTED. | |
| return false; | |
| } | |
| @Override | |
| public boolean isXEmbedSupported() { | |
| return false; | |
| } | |
| @Override | |
| 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; | |
| } | |
| @Override | |
| 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); | |
| } | |
| @Override | |
| 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; | |
| } | |
| @Override | |
| public final long getSourceWindow() { | |
| return sourceWindow; | |
| } | |
| /** | |
| * Reset the state of the object. | |
| */ | |
| @Override | |
| 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; | |
| } | |
| @Override | |
| 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); | |
| } | |
| } |