blob: 85cd5448fd533fca470453fb159cd5e207c0c348 [file] [log] [blame]
/*
* Copyright (c) 2003, 2010, 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.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.InvalidDnDOperationException;
import java.util.Map;
import sun.misc.Unsafe;
/**
* XDragSourceProtocol implementation for Motif DnD protocol.
*
* @since 1.5
*/
class MotifDnDDragSourceProtocol extends XDragSourceProtocol
implements XEventDispatcher {
private static final Unsafe unsafe = XlibWrapper.unsafe;
private long targetEnterServerTime = XConstants.CurrentTime;
protected MotifDnDDragSourceProtocol(XDragSourceProtocolListener listener) {
super(listener);
XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), this);
}
/**
* Creates an instance associated with the specified listener.
*
* @throws NullPointerException if listener is <code>null</code>.
*/
static XDragSourceProtocol createInstance(XDragSourceProtocolListener listener) {
return new MotifDnDDragSourceProtocol(listener);
}
public String getProtocolName() {
return XDragAndDropProtocols.MotifDnD;
}
protected void initializeDragImpl(int actions, Transferable contents,
Map formatMap, long[] formats)
throws InvalidDnDOperationException,
IllegalArgumentException, XException {
long window = XDragSourceProtocol.getDragSourceWindow();
/* Write the Motif DnD initiator info on the root XWindow. */
try {
int index = MotifDnDConstants.getIndexForTargetList(formats);
MotifDnDConstants.writeDragInitiatorInfoStruct(window, index);
} catch (XException xe) {
cleanup();
throw xe;
} catch (InvalidDnDOperationException idoe) {
cleanup();
throw idoe;
}
if (!MotifDnDConstants.MotifDnDSelection.setOwner(contents, formatMap,
formats,
XConstants.CurrentTime)) {
cleanup();
throw new InvalidDnDOperationException("Cannot acquire selection ownership");
}
}
/**
* Processes the specified client message event.
*
* @returns true if the event was successfully processed.
*/
public boolean processClientMessage(XClientMessageEvent xclient) {
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 byteOrder = unsafe.getByte(data + 1);
boolean swapNeeded = byteOrder != MotifDnDConstants.getByteOrderByte();
int action = DnDConstants.ACTION_NONE;
int x = 0;
int y = 0;
/* Only receiver messages should be handled. */
if (origin != MotifDnDConstants.MOTIF_MESSAGE_FROM_RECEIVER) {
return false;
}
switch (reason) {
case MotifDnDConstants.DROP_SITE_ENTER:
case MotifDnDConstants.DROP_SITE_LEAVE:
case MotifDnDConstants.DRAG_MOTION:
case MotifDnDConstants.OPERATION_CHANGED:
break;
default:
// Unknown reason.
return false;
}
int t = unsafe.getInt(data + 4);
if (swapNeeded) {
t = MotifDnDConstants.Swapper.swap(t);
}
long time = t & 0xffffffffL;
// with correction of (32-bit unsigned to 64-bit signed) implicit conversion.
/* Discard events from the previous receiver. */
if (targetEnterServerTime == XConstants.CurrentTime ||
time < targetEnterServerTime) {
return true;
}
if (reason != MotifDnDConstants.DROP_SITE_LEAVE) {
short flags = unsafe.getShort(data + 2);
if (swapNeeded) {
flags = MotifDnDConstants.Swapper.swap(flags);
}
byte status = (byte)((flags & MotifDnDConstants.MOTIF_DND_STATUS_MASK) >>
MotifDnDConstants.MOTIF_DND_STATUS_SHIFT);
byte motif_action = (byte)((flags & MotifDnDConstants.MOTIF_DND_ACTION_MASK) >>
MotifDnDConstants.MOTIF_DND_ACTION_SHIFT);
if (status == MotifDnDConstants.MOTIF_VALID_DROP_SITE) {
action = MotifDnDConstants.getJavaActionsForMotifActions(motif_action);
} else {
action = DnDConstants.ACTION_NONE;
}
short tx = unsafe.getShort(data + 8);
short ty = unsafe.getShort(data + 10);
if (swapNeeded) {
tx = MotifDnDConstants.Swapper.swap(tx);
ty = MotifDnDConstants.Swapper.swap(ty);
}
x = tx;
y = ty;
}
getProtocolListener().handleDragReply(action, x, y);
return true;
}
public TargetWindowInfo getTargetWindowInfo(long window) {
assert XToolkit.isAWTLockHeldByCurrentThread();
WindowPropertyGetter wpg =
new WindowPropertyGetter(window,
MotifDnDConstants.XA_MOTIF_DRAG_RECEIVER_INFO,
0, 0xFFFF, false,
XConstants.AnyPropertyType);
try {
int 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) {
long data = wpg.getData();
byte byteOrderByte = unsafe.getByte(data);
byte dragProtocolStyle = unsafe.getByte(data + 2);
switch (dragProtocolStyle) {
case MotifDnDConstants.MOTIF_PREFER_PREREGISTER_STYLE :
case MotifDnDConstants.MOTIF_PREFER_DYNAMIC_STYLE :
case MotifDnDConstants.MOTIF_DYNAMIC_STYLE :
case MotifDnDConstants.MOTIF_PREFER_RECEIVER_STYLE :
int proxy = unsafe.getInt(data + 4);
if (byteOrderByte != MotifDnDConstants.getByteOrderByte()) {
proxy = MotifDnDConstants.Swapper.swap(proxy);
}
int protocolVersion = unsafe.getByte(data + 1);
return new TargetWindowInfo(proxy, protocolVersion);
default:
// Unsupported protocol style.
return null;
}
} else {
return null;
}
} finally {
wpg.dispose();
}
}
public void sendEnterMessage(long[] formats,
int sourceAction, int sourceActions, long time) {
assert XToolkit.isAWTLockHeldByCurrentThread();
assert getTargetWindow() != 0;
assert formats != null;
targetEnterServerTime = time;
XClientMessageEvent msg = new XClientMessageEvent();
try {
msg.set_type(XConstants.ClientMessage);
msg.set_window(getTargetWindow());
msg.set_format(8);
msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
long data = msg.get_data();
int flags =
(MotifDnDConstants.getMotifActionsForJavaActions(sourceAction) <<
MotifDnDConstants.MOTIF_DND_ACTION_SHIFT) |
(MotifDnDConstants.getMotifActionsForJavaActions(sourceActions) <<
MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT);
unsafe.putByte(data,
(byte)(MotifDnDConstants.TOP_LEVEL_ENTER |
MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
unsafe.putByte(data + 1,
MotifDnDConstants.getByteOrderByte());
unsafe.putShort(data + 2, (short)flags);
unsafe.putInt(data + 4, (int)time);
unsafe.putInt(data + 8, (int)XDragSourceProtocol.getDragSourceWindow());
unsafe.putInt(data + 12, (int)MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom());
XlibWrapper.XSendEvent(XToolkit.getDisplay(),
getTargetProxyWindow(),
false, XConstants.NoEventMask,
msg.pData);
} finally {
msg.dispose();
}
}
public void sendMoveMessage(int xRoot, int yRoot,
int sourceAction, int sourceActions, long time) {
assert XToolkit.isAWTLockHeldByCurrentThread();
assert getTargetWindow() != 0;
XClientMessageEvent msg = new XClientMessageEvent();
try {
msg.set_type(XConstants.ClientMessage);
msg.set_window(getTargetWindow());
msg.set_format(8);
msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
long data = msg.get_data();
int flags =
(MotifDnDConstants.getMotifActionsForJavaActions(sourceAction) <<
MotifDnDConstants.MOTIF_DND_ACTION_SHIFT) |
(MotifDnDConstants.getMotifActionsForJavaActions(sourceActions) <<
MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT);
unsafe.putByte(data,
(byte)(MotifDnDConstants.DRAG_MOTION |
MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
unsafe.putByte(data + 1,
MotifDnDConstants.getByteOrderByte());
unsafe.putShort(data + 2, (short)flags);
unsafe.putInt(data + 4, (int)time);
unsafe.putShort(data + 8, (short)xRoot);
unsafe.putShort(data + 10, (short)yRoot);
XlibWrapper.XSendEvent(XToolkit.getDisplay(),
getTargetProxyWindow(),
false, XConstants.NoEventMask,
msg.pData);
} finally {
msg.dispose();
}
}
public void sendLeaveMessage(long time) {
assert XToolkit.isAWTLockHeldByCurrentThread();
assert getTargetWindow() != 0;
XClientMessageEvent msg = new XClientMessageEvent();
try {
msg.set_type(XConstants.ClientMessage);
msg.set_window(getTargetWindow());
msg.set_format(8);
msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
long data = msg.get_data();
unsafe.putByte(data,
(byte)(MotifDnDConstants.TOP_LEVEL_LEAVE |
MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
unsafe.putByte(data + 1,
MotifDnDConstants.getByteOrderByte());
unsafe.putShort(data + 2, (short)0);
unsafe.putInt(data + 4, (int)time);
unsafe.putInt(data + 8, (int)XDragSourceProtocol.getDragSourceWindow());
XlibWrapper.XSendEvent(XToolkit.getDisplay(),
getTargetProxyWindow(),
false, XConstants.NoEventMask,
msg.pData);
} finally {
msg.dispose();
}
}
protected void sendDropMessage(int xRoot, int yRoot,
int sourceAction, int sourceActions,
long time) {
assert XToolkit.isAWTLockHeldByCurrentThread();
assert getTargetWindow() != 0;
/*
* Motif drop sites expect TOP_LEVEL_LEAVE before DROP_START.
*/
sendLeaveMessage(time);
XClientMessageEvent msg = new XClientMessageEvent();
try {
msg.set_type(XConstants.ClientMessage);
msg.set_window(getTargetWindow());
msg.set_format(8);
msg.set_message_type(MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom());
long data = msg.get_data();
int flags =
(MotifDnDConstants.getMotifActionsForJavaActions(sourceAction) <<
MotifDnDConstants.MOTIF_DND_ACTION_SHIFT) |
(MotifDnDConstants.getMotifActionsForJavaActions(sourceActions) <<
MotifDnDConstants.MOTIF_DND_ACTIONS_SHIFT);
unsafe.putByte(data,
(byte)(MotifDnDConstants.DROP_START |
MotifDnDConstants.MOTIF_MESSAGE_FROM_INITIATOR));
unsafe.putByte(data + 1,
MotifDnDConstants.getByteOrderByte());
unsafe.putShort(data + 2, (short)flags);
unsafe.putInt(data + 4, (int)time);
unsafe.putShort(data + 8, (short)xRoot);
unsafe.putShort(data + 10, (short)yRoot);
unsafe.putInt(data + 12, (int)MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom());
unsafe.putInt(data + 16, (int)XDragSourceProtocol.getDragSourceWindow());
XlibWrapper.XSendEvent(XToolkit.getDisplay(),
getTargetProxyWindow(),
false, XConstants.NoEventMask,
msg.pData);
} finally {
msg.dispose();
}
}
public boolean processProxyModeEvent(XClientMessageEvent xclient,
long sourceWindow) {
// Motif DnD for XEmbed is not implemented.
return false;
}
public void cleanupTargetInfo() {
super.cleanupTargetInfo();
targetEnterServerTime = XConstants.CurrentTime;
}
public void dispatchEvent(XEvent ev) {
switch (ev.get_type()) {
case XConstants.SelectionRequest:
XSelectionRequestEvent xsre = ev.get_xselectionrequest();
long atom = xsre.get_selection();
if (atom == MotifDnDConstants.XA_MOTIF_ATOM_0.getAtom()) {
long target = xsre.get_target();
if (target == MotifDnDConstants.XA_XmTRANSFER_SUCCESS.getAtom()) {
getProtocolListener().handleDragFinished(true);
} else if (target == MotifDnDConstants.XA_XmTRANSFER_FAILURE.getAtom()) {
getProtocolListener().handleDragFinished(false);
}
}
break;
}
}
}