blob: 4edc454df82419b59b54e7bd74af984306c83e8a [file] [log] [blame]
/*
* Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include "awt_dnd.h"
/* Declares getCursor(JNIEnv, jobject) */
#include "awt_Cursor.h"
/* Define java constants */
#include "java_awt_dnd_DnDConstants.h"
#include "sun_awt_dnd_SunDragSourceContextPeer.h"
/* Define DECLARE_* macros */
#include "awt_DataTransferer.h"
#define GRAB_EVENT_MASK \
(ButtonPressMask | ButtonMotionMask | ButtonReleaseMask)
/* Events selected on the root window during drag. */
#define ROOT_EVENT_MASK \
(ButtonMotionMask | KeyPressMask | KeyReleaseMask)
/* Events selected on registered receiver windows during drag. */
#define RECEIVER_EVENT_MASK \
(StructureNotifyMask)
/* in canvas.c */
extern jint getModifiers(uint32_t state, jint button, jint keyCode);
typedef struct {
CARD8 byte_order;
CARD8 protocol_version;
CARD16 index;
CARD32 selection_atom;
} InitiatorInfo;
typedef enum {
/*
* Communicate with receivers of both protocols.
* If the receiver supports both protocols,
* choose Motif DnD for communication.
*/
DS_POLICY_PREFER_MOTIF,
/*
* Communicate with receivers of both protocols.
* If the receiver supports both protocols,
* choose XDnD for communication. [default]
*/
DS_POLICY_PREFER_XDND,
/* Communicate only with Motif DnD receivers. */
DS_POLICY_ONLY_MOTIF,
/* Communicate only with XDnD receivers. */
DS_POLICY_ONLY_XDND
} DragSourcePolicy;
/* The drag source policy. */
static DragSourcePolicy drag_source_policy = DS_POLICY_PREFER_XDND;
static Boolean dnd_in_progress = False;
static Boolean drag_in_progress = False;
static jobject source_peer = NULL;
static Atom* data_types = NULL;
static unsigned int data_types_count = 0;
static Window drag_root_window = None;
static EventMask your_root_event_mask = NoEventMask;
static Time latest_time_stamp = CurrentTime;
/* The child of the root which is currently under the mouse. */
static Window target_root_subwindow = None;
static Window target_window = None;
static long target_window_mask = 0;
static Window target_proxy_window = None;
static Protocol target_protocol = NO_PROTOCOL;
static unsigned int target_protocol_version = 0;
/*
* The server time when the pointer entered the current target -
* needed on Motif DnD to filter out messages from the previous
* target.
* It is updated whenever the target_window is updated.
* If the target_window is set to non-None, it is set to the time stamp
* of the X event that trigger the update. Otherwise, it is set to CurrentTime.
*/
static Time target_enter_server_time = CurrentTime;
static int x_root = 0;
static int y_root = 0;
static unsigned int event_state = 0;
static jint source_action = java_awt_dnd_DnDConstants_ACTION_NONE;
static jint source_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
static jint target_action = java_awt_dnd_DnDConstants_ACTION_NONE;
/* Forward declarations */
static void cleanup_drag(Display* dpy, Time time);
static Boolean process_proxy_mode_event(XEvent* xev);
/**************************** XEmbed server DnD support ***********************/
static Window proxy_mode_source_window = None;
/******************************************************************************/
/**************************** JNI stuff ***************************************/
DECLARE_JAVA_CLASS(dscp_clazz, "sun/awt/dnd/SunDragSourceContextPeer")
static void
ds_postDragSourceDragEvent(JNIEnv* env, jint targetAction, unsigned int state,
int x, int y, jint dispatch_type) {
DECLARE_VOID_JAVA_METHOD(dscp_postDragSourceDragEvent, dscp_clazz,
"postDragSourceDragEvent", "(IIIII)V");
DASSERT(!JNU_IsNull(env, source_peer));
if (JNU_IsNull(env, source_peer)) {
return;
}
(*env)->CallVoidMethod(env, source_peer, dscp_postDragSourceDragEvent,
targetAction, getModifiers(state, 0, 0), x, y,
dispatch_type);
}
static jint
ds_convertModifiersToDropAction(JNIEnv* env, unsigned int state) {
jint action;
DECLARE_STATIC_JINT_JAVA_METHOD(dscp_convertModifiersToDropAction, dscp_clazz,
"convertModifiersToDropAction", "(II)I");
action = (*env)->CallStaticIntMethod(env, clazz, dscp_convertModifiersToDropAction,
getModifiers(state, 0, 0), source_actions);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return java_awt_dnd_DnDConstants_ACTION_NONE;
}
return action;
}
static void
ds_postDragSourceEvent(JNIEnv* env, int x, int y) {
DECLARE_VOID_JAVA_METHOD(dscp_dragExit, dscp_clazz,
"dragExit", "(II)V");
DASSERT(!JNU_IsNull(env, source_peer));
if (JNU_IsNull(env, source_peer)) {
return;
}
(*env)->CallVoidMethod(env, source_peer, dscp_dragExit, x, y);
}
static void
ds_postDragSourceDropEvent(JNIEnv* env, jboolean success, jint targetAction,
int x, int y) {
DECLARE_VOID_JAVA_METHOD(dscp_dragDropFinished, dscp_clazz,
"dragDropFinished", "(ZIII)V");
DASSERT(!JNU_IsNull(env, source_peer));
if (JNU_IsNull(env, source_peer)) {
return;
}
(*env)->CallVoidMethod(env, source_peer, dscp_dragDropFinished,
success, targetAction, x, y);
}
/******************************************************************************/
static void
cancel_drag(XtPointer client_data, XtIntervalId* id) {
Time time_stamp = awt_util_getCurrentServerTime();
cleanup_drag(awt_display, time_stamp);
}
#define DONT_CARE -1
static void
awt_popupCallback(Widget shell, XtPointer closure, XtPointer call_data) {
XtGrabKind grab_kind = XtGrabNone;
if (call_data != NULL) {
grab_kind = *((XtGrabKind*)call_data);
}
if (XmIsVendorShell(shell)) {
int input_mode;
XtVaGetValues(shell, XmNmwmInputMode, &input_mode, NULL);
switch (input_mode) {
case DONT_CARE:
case MWM_INPUT_MODELESS:
grab_kind = XtGrabNonexclusive; break;
case MWM_INPUT_PRIMARY_APPLICATION_MODAL:
case MWM_INPUT_SYSTEM_MODAL:
case MWM_INPUT_FULL_APPLICATION_MODAL:
grab_kind = XtGrabExclusive; break;
}
}
if (grab_kind == XtGrabExclusive) {
/*
* We should cancel the drag on the toolkit thread. Otherwise, it can be
* called while the toolkit thread is waiting inside some drag callback.
* In this case Motif will crash when the drag callback returns.
*/
XtAppAddTimeOut(awt_appContext, 0L, cancel_drag, NULL);
}
}
static XtInitProc xt_shell_initialize = NULL;
static void
awt_ShellInitialize(Widget req, Widget new, ArgList args, Cardinal *num_args) {
XtAddCallback(new, XtNpopupCallback, awt_popupCallback, NULL);
(*xt_shell_initialize)(req, new, args, num_args);
}
/*
* Fix for 4484572 (copied from awt_XmDnD.c).
* Modify the 'initialize' routine for all ShellWidget instances, so that it
* will install an XtNpopupCallback that cancels the current drag operation.
* It is needed, since AWT doesn't have full control over all ShellWidget
* instances (e.g. XmPopupMenu internally creates and popups an XmMenuShell).
*/
static void
awt_set_ShellInitialize() {
static Boolean inited = False;
DASSERT(!inited);
if (inited) {
return;
}
xt_shell_initialize = shellWidgetClass->core_class.initialize;
shellWidgetClass->core_class.initialize = (XtInitProc)awt_ShellInitialize;
inited = True;
}
/*
* Returns True if initialization completes successfully.
*/
Boolean
awt_dnd_ds_init(Display* display) {
if (XSaveContext(display, XA_XdndSelection, awt_convertDataContext,
(XPointer)NULL) == XCNOMEM) {
return False;
}
if (XSaveContext(display, _XA_MOTIF_ATOM_0, awt_convertDataContext,
(XPointer)NULL) == XCNOMEM) {
return False;
}
{
char *ev = getenv("_JAVA_DRAG_SOURCE_POLICY");
/* By default XDnD protocol is preferred. */
drag_source_policy = DS_POLICY_PREFER_XDND;
if (ev != NULL) {
if (strcmp(ev, "PREFER_XDND") == 0) {
drag_source_policy = DS_POLICY_PREFER_XDND;
} else if (strcmp(ev, "PREFER_MOTIF") == 0) {
drag_source_policy = DS_POLICY_PREFER_MOTIF;
} else if (strcmp(ev, "ONLY_MOTIF") == 0) {
drag_source_policy = DS_POLICY_ONLY_MOTIF;
} else if (strcmp(ev, "ONLY_XDND") == 0) {
drag_source_policy = DS_POLICY_ONLY_XDND;
}
}
}
awt_set_ShellInitialize();
return True;
}
/*
* Returns a handle of the window used as a drag source.
*/
Window
awt_dnd_ds_get_source_window() {
return get_awt_root_window();
}
/*
* Returns True if a drag operation initiated by this client
* is still in progress.
*/
Boolean
awt_dnd_ds_in_progress() {
return dnd_in_progress;
}
static void
ds_send_event_to_target(XClientMessageEvent* xclient) {
/* Shortcut if the source is in the same JVM. */
if (XtWindowToWidget(xclient->display, target_proxy_window) != NULL) {
awt_dnd_dt_process_event((XEvent*)xclient);
} else {
XSendEvent(xclient->display, target_proxy_window, False, NoEventMask,
(XEvent*)xclient);
}
}
static void
xdnd_send_enter(Display* dpy, Time time) {
XClientMessageEvent enter;
enter.display = dpy;
enter.type = ClientMessage;
enter.window = target_window;
enter.format = 32;
enter.message_type = XA_XdndEnter;
enter.data.l[0] = awt_dnd_ds_get_source_window();
enter.data.l[1] = target_protocol_version << XDND_PROTOCOL_SHIFT;
enter.data.l[1] |= data_types_count > 3 ? XDND_DATA_TYPES_BIT : 0;
enter.data.l[2] = data_types_count > 0 ? data_types[0] : None;
enter.data.l[3] = data_types_count > 1 ? data_types[1] : None;
enter.data.l[4] = data_types_count > 2 ? data_types[2] : None;
ds_send_event_to_target(&enter);
}
static void
motif_send_enter(Display* dpy, Time time) {
XClientMessageEvent enter;
enter.display = dpy;
enter.type = ClientMessage;
enter.window = target_window;
enter.format = 8;
enter.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
{
void* p = &enter.data.b[0];
int flags = 0;
flags |= java_to_motif_actions(source_action) << MOTIF_DND_ACTION_SHIFT;
flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
write_card8(&p, TOP_LEVEL_ENTER | MOTIF_MESSAGE_FROM_INITIATOR);
write_card8(&p, MOTIF_BYTE_ORDER);
write_card16(&p, flags);
write_card32(&p, time);
write_card32(&p, awt_dnd_ds_get_source_window());
write_card32(&p, _XA_MOTIF_ATOM_0);
}
ds_send_event_to_target(&enter);
}
static void
send_enter(Display* dpy, Time time) {
switch (target_protocol) {
case XDND_PROTOCOL:
xdnd_send_enter(dpy, time);
break;
case MOTIF_DND_PROTOCOL:
motif_send_enter(dpy, time);
break;
case NO_PROTOCOL:
default:
DTRACE_PRINTLN2("%s:%d send_enter: unknown DnD protocol.", __FILE__, __LINE__);
break;
}
}
static void
xdnd_send_move(XMotionEvent* event) {
XClientMessageEvent move;
move.display = event->display;
move.type = ClientMessage;
move.window = target_window;
move.format = 32;
move.message_type = XA_XdndPosition;
move.data.l[0] = awt_dnd_ds_get_source_window();
move.data.l[1] = 0; /* flags */
move.data.l[2] = event->x_root << 16 | event->y_root;
move.data.l[3] = event->time;
move.data.l[4] = java_to_xdnd_action(source_action);
ds_send_event_to_target(&move);
}
static void
motif_send_move(XMotionEvent* event) {
XClientMessageEvent move;
move.display = event->display;
move.type = ClientMessage;
move.window = target_window;
move.format = 8;
move.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
{
void* p = move.data.b;
int flags = 0;
flags |= java_to_motif_actions(source_action) << MOTIF_DND_ACTION_SHIFT;
flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
write_card8(&p, DRAG_MOTION | MOTIF_MESSAGE_FROM_INITIATOR);
write_card8(&p, MOTIF_BYTE_ORDER);
write_card16(&p, flags);
write_card32(&p, event->time);
write_card16(&p, event->x_root);
write_card16(&p, event->y_root);
}
ds_send_event_to_target(&move);
}
static void
send_move(XMotionEvent* event) {
switch (target_protocol) {
case XDND_PROTOCOL:
xdnd_send_move(event);
break;
case MOTIF_DND_PROTOCOL:
motif_send_move(event);
break;
case NO_PROTOCOL:
default:
DTRACE_PRINTLN2("%s:%d send_move: unknown DnD protocol.", __FILE__, __LINE__);
break;
}
}
static void
xdnd_send_leave(Display* dpy, Time time) {
XClientMessageEvent leave;
leave.display = dpy;
leave.type = ClientMessage;
leave.window = target_window;
leave.format = 32;
leave.message_type = XA_XdndLeave;
leave.data.l[0] = awt_dnd_ds_get_source_window();
leave.data.l[1] = 0;
leave.data.l[2] = 0;
leave.data.l[3] = 0;
leave.data.l[4] = 0;
ds_send_event_to_target(&leave);
}
static void
motif_send_leave(Display* dpy, Time time) {
XClientMessageEvent leave;
leave.display = dpy;
leave.type = ClientMessage;
leave.window = target_window;
leave.format = 8;
leave.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
{
void* p = &leave.data.b[0];
write_card8(&p, TOP_LEVEL_LEAVE | MOTIF_MESSAGE_FROM_INITIATOR);
write_card8(&p, MOTIF_BYTE_ORDER);
write_card16(&p, 0);
write_card32(&p, time);
write_card32(&p, awt_dnd_ds_get_source_window());
}
ds_send_event_to_target(&leave);
}
static void
send_leave(Display* dpy, Time time) {
switch (target_protocol) {
case XDND_PROTOCOL:
xdnd_send_leave(dpy, time);
break;
case MOTIF_DND_PROTOCOL:
motif_send_leave(dpy, time);
break;
case NO_PROTOCOL:
default:
DTRACE_PRINTLN2("%s:%d send_leave: unknown DnD protocol.", __FILE__, __LINE__);
break;
}
}
static void
xdnd_send_drop(XButtonEvent* event) {
XClientMessageEvent drop;
drop.display = event->display;
drop.type = ClientMessage;
drop.window = target_window;
drop.format = 32;
drop.message_type = XA_XdndDrop;
drop.data.l[0] = awt_dnd_ds_get_source_window();
drop.data.l[1] = 0; /* flags */
drop.data.l[2] = event->time; /* ### */
drop.data.l[3] = 0;
drop.data.l[4] = 0;
ds_send_event_to_target(&drop);
}
static void
motif_send_drop(XButtonEvent* event) {
XClientMessageEvent drop;
/*
* Motif drop sites expect TOP_LEVEL_LEAVE before DROP_START.
*/
motif_send_leave(event->display, event->time);
drop.display = event->display;
drop.type = ClientMessage;
drop.window = target_window;
drop.format = 8;
drop.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
{
void* p = &drop.data.b[0];
int flags = 0;
flags |= java_to_motif_actions(source_action) << MOTIF_DND_ACTION_SHIFT;
flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
write_card8(&p, DROP_START | MOTIF_MESSAGE_FROM_INITIATOR);
write_card8(&p, MOTIF_BYTE_ORDER);
write_card16(&p, flags);
write_card32(&p, event->time);
write_card16(&p, event->x_root);
write_card16(&p, event->y_root);
write_card32(&p, _XA_MOTIF_ATOM_0);
write_card32(&p, awt_dnd_ds_get_source_window());
}
ds_send_event_to_target(&drop);
}
static void
send_drop(XButtonEvent* event) {
switch (target_protocol) {
case XDND_PROTOCOL:
xdnd_send_drop(event);
break;
case MOTIF_DND_PROTOCOL:
motif_send_drop(event);
break;
case NO_PROTOCOL:
default:
DTRACE_PRINTLN2("%s:%d send_drop: unknown DnD protocol.", __FILE__, __LINE__);
break;
}
}
static void
remove_dnd_grab(Display* dpy, Time time) {
XUngrabPointer(dpy, time);
XUngrabKeyboard(dpy, time);
/* Restore the root event mask if it was changed. */
if ((your_root_event_mask | ROOT_EVENT_MASK) != your_root_event_mask &&
drag_root_window != None) {
XSelectInput(dpy, drag_root_window, your_root_event_mask);
drag_root_window = None;
your_root_event_mask = NoEventMask;
}
}
static void
cleanup_target_info(Display* dpy) {
target_root_subwindow = None;
target_window = None;
target_proxy_window = None;
target_protocol = NO_PROTOCOL;
target_protocol_version = 0;
target_enter_server_time = CurrentTime;
target_action = java_awt_dnd_DnDConstants_ACTION_NONE;
}
static void
cleanup_drag(Display* dpy, Time time) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
if (dnd_in_progress) {
if (target_window != None) {
send_leave(dpy, time);
}
if (target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
ds_postDragSourceEvent(env, x_root, y_root);
}
ds_postDragSourceDropEvent(env, JNI_FALSE,
java_awt_dnd_DnDConstants_ACTION_NONE,
x_root, y_root);
}
/* Cleanup the global state */
dnd_in_progress = False;
drag_in_progress = False;
data_types_count = 0;
if (data_types != NULL) {
free(data_types);
data_types = NULL;
}
if (!JNU_IsNull(env, source_peer)) {
(*env)->DeleteGlobalRef(env, source_peer);
source_peer = NULL;
}
cleanup_target_info(dpy);
remove_dnd_grab(dpy, time);
XDeleteProperty(awt_display, awt_dnd_ds_get_source_window(), _XA_MOTIF_ATOM_0);
XDeleteProperty(awt_display, awt_dnd_ds_get_source_window(), XA_XdndTypeList);
XDeleteProperty(awt_display, awt_dnd_ds_get_source_window(), XA_XdndActionList);
XtDisownSelection(awt_root_shell, _XA_MOTIF_ATOM_0, time);
XtDisownSelection(awt_root_shell, XA_XdndSelection, time);
awt_cleanupConvertDataContext(env, _XA_MOTIF_ATOM_0);
awt_cleanupConvertDataContext(env, XA_XdndSelection);
}
static void
process_drop(XButtonEvent* event) {
unsigned char ret;
XWindowAttributes xwa;
DASSERT(target_window != None);
XGetWindowAttributes(event->display, target_window, &xwa);
target_window_mask = xwa.your_event_mask;
/* Select for DestoyNotify to cleanup if the target crashes. */
ret = checked_XSelectInput(event->display, target_window,
(target_window_mask | StructureNotifyMask));
if (ret == Success) {
send_drop(event);
} else {
DTRACE_PRINTLN2("%s:%d drop rejected - invalid window.",
__FILE__, __LINE__);
cleanup_drag(event->display, event->time);
}
}
static Window
find_client_window(Display* dpy, Window window) {
Window root, parent, *children;
unsigned int nchildren, idx;
Atom type;
int format;
unsigned long nitems;
unsigned long after;
unsigned char *data;
Status ret;
if (XGetWindowProperty(dpy, window, XA_WM_STATE, 0, 0, False,
AnyPropertyType, &type, &format, &nitems,
&after, &data) == Success) {
XFree(data);
}
if (type != None) {
return window;
}
if (!XQueryTree(dpy, window, &root, &parent, &children, &nchildren)) {
return None;
}
if (children == NULL) {
return None;
}
for (idx = 0; idx < nchildren; idx++) {
Window win = find_client_window(dpy, children[idx]);
if (win != None) {
XFree(children);
return win;
}
}
XFree(children);
return None;
}
static void
do_update_target_window(Display* dpy, Window subwindow, Time time) {
Window client_window = None;
Window proxy_window = None;
Protocol protocol = NO_PROTOCOL;
unsigned int protocol_version = 0;
Boolean is_receiver = False;
client_window = find_client_window(dpy, subwindow);
if (client_window != None) {
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom type;
int format;
unsigned long nitems;
unsigned long after;
unsigned char *data;
/*
* No need for checked_XGetWindowProperty, since we check the returned
* property type anyway.
*/
if (drag_source_policy != DS_POLICY_ONLY_XDND) {
data = NULL;
status = XGetWindowProperty(dpy, client_window,
_XA_MOTIF_DRAG_RECEIVER_INFO,
0, 0xFFFF, False, AnyPropertyType,
&type, &format, &nitems, &after, &data);
if (status == Success && data != NULL && type != None && format == 8
&& nitems >= MOTIF_RECEIVER_INFO_SIZE) {
unsigned char byte_order = read_card8((char*)data, 0);
unsigned char drag_protocol_style = read_card8((char*)data, 2);
switch (drag_protocol_style) {
case MOTIF_PREFER_PREREGISTER_STYLE :
case MOTIF_PREFER_DYNAMIC_STYLE :
case MOTIF_DYNAMIC_STYLE :
case MOTIF_PREFER_RECEIVER_STYLE :
proxy_window = read_card32((char*)data, 4, byte_order);
protocol = MOTIF_DND_PROTOCOL;
protocol_version = read_card8((char*)data, 1);
is_receiver = True;
break;
default:
DTRACE_PRINTLN3("%s:%d unsupported protocol style (%d).",
__FILE__, __LINE__, (int)drag_protocol_style);
}
}
if (status == Success) {
XFree(data);
data = NULL;
}
}
if (drag_source_policy != DS_POLICY_ONLY_MOTIF &&
(drag_source_policy != DS_POLICY_PREFER_MOTIF || !is_receiver)) {
data = NULL;
status = XGetWindowProperty(dpy, client_window, XA_XdndAware, 0, 1,
False, AnyPropertyType, &type, &format,
&nitems, &after, &data);
if (status == Success && data != NULL && type == XA_ATOM) {
unsigned int target_version = *((unsigned int*)data);
if (target_version >= XDND_MIN_PROTOCOL_VERSION) {
proxy_window = None;
protocol = XDND_PROTOCOL;
protocol_version = target_version < XDND_PROTOCOL_VERSION ?
target_version : XDND_PROTOCOL_VERSION;
is_receiver = True;
}
}
/* Retrieve the proxy window handle and check if it is valid. */
if (protocol == XDND_PROTOCOL) {
if (status == Success) {
XFree(data);
}
data = NULL;
status = XGetWindowProperty(dpy, client_window, XA_XdndProxy, 0,
1, False, XA_WINDOW, &type, &format,
&nitems, &after, &data);
if (status == Success && data != NULL && type == XA_WINDOW) {
proxy_window = *((Window*)data);
}
if (proxy_window != None) {
if (status == Success) {
XFree(data);
}
data = NULL;
status = XGetWindowProperty(dpy, proxy_window, XA_XdndProxy,
0, 1, False, XA_WINDOW, &type,
&format, &nitems, &after, &data);
if (status != Success || data == NULL || type != XA_WINDOW ||
*((Window*)data) != proxy_window) {
proxy_window = None;
} else {
if (status == Success) {
XFree(data);
}
data = NULL;
status = XGetWindowProperty(dpy, proxy_window,
XA_XdndAware, 0, 1, False,
AnyPropertyType, &type,
&format, &nitems, &after,
&data);
if (status != Success || data == NULL || type != XA_ATOM) {
proxy_window = None;
}
}
}
}
XFree(data);
}
if (proxy_window == None) {
proxy_window = client_window;
}
}
if (is_receiver) {
target_window = client_window;
target_proxy_window = proxy_window;
target_protocol = protocol;
target_protocol_version = protocol_version;
} else {
target_window = None;
target_proxy_window = None;
target_protocol = NO_PROTOCOL;
target_protocol_version = 0;
}
target_action = java_awt_dnd_DnDConstants_ACTION_NONE;
if (target_window != None) {
target_enter_server_time = time;
} else {
target_enter_server_time = CurrentTime;
}
target_root_subwindow = subwindow;
}
static void
update_target_window(XMotionEvent* event) {
Display* dpy = event->display;
int x = event->x_root;
int y = event->x_root;
Time time = event->time;
Window subwindow = event->subwindow;
/*
* If this event had occurred before the pointer was grabbed,
* query the server for the current root subwindow.
*/
if (event->window != event->root) {
int xw, yw, xr, yr;
unsigned int modifiers;
XQueryPointer(dpy, event->root, &event->root, &subwindow,
&xr, &yr, &xw, &yw, &modifiers);
}
if (target_root_subwindow != subwindow) {
if (target_window != None) {
send_leave(dpy, time);
/*
* Neither Motif DnD nor XDnD provide a mean for the target
* to notify the source that the pointer exits the drop site
* that occupies the whole top level.
* We detect this situation and post dragExit.
*/
if (target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
ds_postDragSourceEvent(env, x, y);
}
}
/* Update the global state. */
do_update_target_window(dpy, subwindow, time);
if (target_window != None) {
send_enter(dpy, time);
}
}
}
/*
* Updates the source action based on the specified event state.
* Returns True if source action changed, False otherwise.
*/
static Boolean
update_source_action(unsigned int state) {
JNIEnv* env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
jint action = ds_convertModifiersToDropAction(env, state);
if (source_action == action) {
return False;
}
source_action = action;
return True;
}
static void
handle_mouse_move(XMotionEvent* event) {
if (!drag_in_progress) {
return;
}
if (x_root != event->x_root || y_root != event->y_root) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
ds_postDragSourceDragEvent(env, target_action, event->state,
event->x_root, event->y_root,
sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_MOUSE_MOVED);
x_root = event->x_root;
y_root = event->y_root;
}
if (event_state != event->state) {
if (update_source_action(event->state) && target_window != None) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
ds_postDragSourceDragEvent(env, target_action, event->state,
event->x_root, event->y_root,
sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_CHANGED);
}
event_state = event->state;
}
update_target_window(event);
if (target_window != None) {
send_move(event);
}
}
static Boolean
handle_xdnd_status(XClientMessageEvent* event) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
long* event_data = event->data.l;
Window target_win = None;
jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
DTRACE_PRINTLN4("%s:%d XdndStatus target_window=%ld target_protocol=%d.",
__FILE__, __LINE__, target_window, target_protocol);
if (target_protocol != XDND_PROTOCOL) {
DTRACE_PRINTLN2("%s:%d XdndStatus rejected - invalid state.",
__FILE__, __LINE__);
return True;
}
target_win = event_data[0];
/* Ignore XDnD messages from all other windows. */
if (target_window != target_win) {
DTRACE_PRINTLN4("%s:%d XdndStatus rejected - invalid target window cur=%ld this=%ld.",
__FILE__, __LINE__, target_window, target_win);
return True;
}
if (event_data[1] & XDND_ACCEPT_DROP_FLAG) {
/* This feature is new in XDnD version 2, but we can use it as XDnD
compliance only requires supporting version 3 and up. */
action = xdnd_to_java_action(event_data[4]);
}
if (action == java_awt_dnd_DnDConstants_ACTION_NONE &&
target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
ds_postDragSourceEvent(env, x_root, y_root);
} else if (action != java_awt_dnd_DnDConstants_ACTION_NONE) {
jint type = 0;
if (target_action == java_awt_dnd_DnDConstants_ACTION_NONE) {
type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_ENTER;
} else {
type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_MOTION;
}
ds_postDragSourceDragEvent(env, action, event_state,
x_root, y_root, type);
}
target_action = action;
return True;
}
static Boolean
handle_xdnd_finished(XClientMessageEvent* event) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
long* event_data = event->data.l;
Window target_win = None;
jboolean success = JNI_TRUE;
jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
if (target_protocol != XDND_PROTOCOL) {
DTRACE_PRINTLN2("%s:%d XdndStatus rejected - invalid state.",
__FILE__, __LINE__);
return True;
}
target_win = event_data[0];
/* Ignore XDnD messages from all other windows. */
if (target_window != target_win) {
DTRACE_PRINTLN4("%s:%d XdndStatus rejected - invalid target window cur=%ld this=%ld.",
__FILE__, __LINE__, target_window, target_win);
return True;
}
if (target_protocol_version >= 5) {
success = (event_data[1] & XDND_ACCEPT_DROP_FLAG) != 0 ?
JNI_TRUE : JNI_FALSE;
action = xdnd_to_java_action(event_data[2]);
} else {
/* Assume that the drop was successful and the performed drop action is
the drop action accepted with the latest XdndStatus message. */
success = JNI_TRUE;
action = target_action;
}
ds_postDragSourceDropEvent(env, success, action, x_root, y_root);
dnd_in_progress = False;
XSelectInput(event->display, target_win, target_window_mask);
cleanup_drag(event->display, CurrentTime);
return True;
}
static Boolean
handle_motif_client_message(XClientMessageEvent* event) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
int reason = (int)(event->data.b[0] & MOTIF_MESSAGE_REASON_MASK);
int origin = (int)(event->data.b[0] & MOTIF_MESSAGE_SENDER_MASK);
unsigned char byte_order = event->data.b[1];
jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
Time time = CurrentTime;
int x = 0, y = 0;
/* Only receiver messages should be handled. */
if (origin != MOTIF_MESSAGE_FROM_RECEIVER) {
return False;
}
if (target_protocol != MOTIF_DND_PROTOCOL) {
DTRACE_PRINTLN2("%s:%d _MOTIF_DRAG_AND_DROP_MESSAGE rejected - invalid state.",
__FILE__, __LINE__);
return True;
}
switch (reason) {
case DROP_SITE_ENTER:
case DROP_SITE_LEAVE:
case DRAG_MOTION:
case OPERATION_CHANGED:
break;
default:
return False;
}
time = read_card32(event->data.b, 4, byte_order);
/* Discard events from the previous receiver. */
if (target_enter_server_time == CurrentTime ||
time < target_enter_server_time) {
DTRACE_PRINTLN2("%s:%d _MOTIF_DRAG_AND_DROP_MESSAGE rejected - invalid time.",
__FILE__, __LINE__);
return True;
}
if (reason != DROP_SITE_LEAVE) {
CARD16 flags = read_card16(event->data.b, 2, byte_order);
unsigned char status = (flags & MOTIF_DND_STATUS_MASK) >>
MOTIF_DND_STATUS_SHIFT;
unsigned char motif_action = (flags & MOTIF_DND_ACTION_MASK) >>
MOTIF_DND_ACTION_SHIFT;
if (status == MOTIF_VALID_DROP_SITE) {
action = motif_to_java_actions(motif_action);
} else {
action = java_awt_dnd_DnDConstants_ACTION_NONE;
}
x = read_card16(event->data.b, 8, byte_order);
y = read_card16(event->data.b, 10, byte_order);
}
/*
* We should derive the type of java event to post not from the message
* reason, but from the combination of the current and previous target
* actions:
* Even if the reason is DROP_SITE_LEAVE we shouldn't post dragExit
* if the drag was rejected earlier.
* Even if the reason is DROP_SITE_ENTER we shouldn't post dragEnter
* if the drag is not accepted.
*/
if (target_action != java_awt_dnd_DnDConstants_ACTION_NONE &&
action == java_awt_dnd_DnDConstants_ACTION_NONE) {
ds_postDragSourceEvent(env, x, y);
} else if (action != java_awt_dnd_DnDConstants_ACTION_NONE) {
jint type = 0;
if (target_action == java_awt_dnd_DnDConstants_ACTION_NONE) {
type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_ENTER;
} else {
type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_MOTION;
}
ds_postDragSourceDragEvent(env, action, event_state, x, y, type);
}
target_action = action;
return True;
}
/*
* Handles client messages.
* Returns True if the event is processed, False otherwise.
*/
static Boolean
handle_client_message(XClientMessageEvent* event) {
if (event->message_type == XA_XdndStatus) {
return handle_xdnd_status(event);
} else if (event->message_type == XA_XdndFinished) {
return handle_xdnd_finished(event);
} else if (event->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
return handle_motif_client_message(event);
}
return False;
}
/*
* Similar to XtLastTimestampProcessed(). We cannot use Xt time stamp, as it is
* updated in XtDispatchEvent that may not be called if a java event is
* consumed. This can make Xt time stamp out-of-date and cause XGrab* failures
* with GrabInvalidTime reason.
*/
static Time
get_latest_time_stamp() {
return latest_time_stamp;
}
static void
update_latest_time_stamp(XEvent* event) {
Time time = latest_time_stamp;
switch (event->type) {
case KeyPress:
case KeyRelease: time = event->xkey.time; break;
case ButtonPress:
case ButtonRelease: time = event->xbutton.time; break;
case MotionNotify: time = event->xmotion.time; break;
case EnterNotify:
case LeaveNotify: time = event->xcrossing.time; break;
case PropertyNotify: time = event->xproperty.time; break;
case SelectionClear: time = event->xselectionclear.time; break;
}
latest_time_stamp = time;
}
Boolean
awt_dnd_ds_process_event(XEvent* event) {
Display* dpy = event->xany.display;
update_latest_time_stamp(event);
if (process_proxy_mode_event(event)) {
return True;
}
if (!dnd_in_progress) {
return False;
}
/* Process drag and drop messages. */
switch (event->type) {
case ClientMessage:
return handle_client_message(&event->xclient);
case DestroyNotify:
/* Target crashed during drop processing - cleanup. */
if (!drag_in_progress &&
event->xdestroywindow.window == target_window) {
cleanup_drag(dpy, CurrentTime);
return True;
}
/* Pass along */
return False;
}
if (!drag_in_progress) {
return False;
}
/* Process drag-only messages. */
switch (event->type) {
case KeyRelease:
case KeyPress: {
KeySym keysym = XKeycodeToKeysym(dpy, event->xkey.keycode, 0);
switch (keysym) {
case XK_Escape: {
if (keysym == XK_Escape) {
remove_dnd_grab(dpy, event->xkey.time);
cleanup_drag(dpy, event->xkey.time);
}
break;
}
case XK_Control_R:
case XK_Control_L:
case XK_Shift_R:
case XK_Shift_L: {
Window subwindow;
int xw, yw, xr, yr;
unsigned int modifiers;
XQueryPointer(event->xkey.display, event->xkey.root, &event->xkey.root, &subwindow,
&xr, &yr, &xw, &yw, &modifiers);
event->xkey.state = modifiers;
//It's safe to use key event as motion event since we use only their common fields.
handle_mouse_move(&event->xmotion);
break;
}
}
return True;
}
case ButtonPress:
return True;
case MotionNotify:
handle_mouse_move(&event->xmotion);
return True;
case ButtonRelease:
/*
* On some X servers it could happen that ButtonRelease coordinates
* differ from the latest MotionNotify coordinates, so we need to
* process it as a mouse motion.
* MotionNotify differs from ButtonRelease only in is_hint member, but
* we never use it, so it is safe to cast to MotionNotify.
*/
handle_mouse_move(&event->xmotion);
if (event->xbutton.button == Button1 || event->xbutton.button == Button2) {
// drag is initiated with Button1 or Button2 pressed and
// ended on release of either of these buttons (as the same
// behavior was with our old Motif DnD-based implementation)
remove_dnd_grab(dpy, event->xbutton.time);
drag_in_progress = False;
if (target_window != None && target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
/*
* ACTION_NONE indicates that either the drop target rejects the
* drop or it haven't responded yet. The latter could happen in
* case of fast drag, slow target-server connection or slow
* drag notifications processing on the target side.
*/
process_drop(&event->xbutton);
} else {
cleanup_drag(dpy, event->xbutton.time);
}
}
return True;
default:
return False;
}
}
static Boolean
motif_convert_proc(Widget w, Atom* selection, Atom* target, Atom* type,
XtPointer* value, unsigned long* length, int32_t* format) {
if (*target == XA_XmTRANSFER_SUCCESS ||
*target == XA_XmTRANSFER_FAILURE) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
jboolean success =
(*target == XA_XmTRANSFER_SUCCESS) ? JNI_TRUE : JNI_FALSE;
ds_postDragSourceDropEvent(env, success, target_action,
x_root, y_root);
dnd_in_progress = False;
XSelectInput(XtDisplay(w), target_window, target_window_mask);
cleanup_drag(XtDisplay(w), CurrentTime);
*type = *target;
*length = 0;
*format = 32;
*value = NULL;
return True;
} else {
return awt_convertData(w, selection, target, type, value, length,
format);
}
}
static Boolean
set_convert_data_context(JNIEnv* env, Display* dpy, XID xid, jobject component,
jobject transferable, jobject formatMap,
jlongArray formats) {
awt_convertDataCallbackStruct* structPtr = NULL;
if (XFindContext(awt_display, xid, awt_convertDataContext,
(XPointer*)&structPtr) == XCNOMEM || structPtr != NULL) {
return False;
}
structPtr = calloc(1, sizeof(awt_convertDataCallbackStruct));
if (structPtr == NULL) {
return False;
}
structPtr->source = (*env)->NewGlobalRef(env, component);
structPtr->transferable = (*env)->NewGlobalRef(env, transferable);
structPtr->formatMap = (*env)->NewGlobalRef(env, formatMap);
structPtr->formats = (*env)->NewGlobalRef(env, formats);
if (JNU_IsNull(env, structPtr->source) ||
JNU_IsNull(env, structPtr->transferable) ||
JNU_IsNull(env, structPtr->formatMap) ||
JNU_IsNull(env, structPtr->formats)) {
if (!JNU_IsNull(env, structPtr->source)) {
(*env)->DeleteGlobalRef(env, structPtr->source);
}
if (!JNU_IsNull(env, structPtr->transferable)) {
(*env)->DeleteGlobalRef(env, structPtr->transferable);
}
if (!JNU_IsNull(env, structPtr->formatMap)) {
(*env)->DeleteGlobalRef(env, structPtr->formatMap);
}
if (!JNU_IsNull(env, structPtr->formats)) {
(*env)->DeleteGlobalRef(env, structPtr->formats);
}
free(structPtr);
return False;
}
if (XSaveContext(dpy, xid, awt_convertDataContext,
(XPointer)structPtr) == XCNOMEM) {
free(structPtr);
return False;
}
return True;
}
/*
* Convenience routine. Constructs an appropriate exception message based on the
* specified prefix and the return code of XGrab* function and throws an
* InvalidDnDOperationException with the constructed message.
*/
static void
throw_grab_failure_exception(JNIEnv* env, int ret_code, char* msg_prefix) {
char msg[200];
char* msg_cause = "";
switch (ret_code) {
case GrabNotViewable: msg_cause = "not viewable"; break;
case AlreadyGrabbed: msg_cause = "already grabbed"; break;
case GrabInvalidTime: msg_cause = "invalid time"; break;
case GrabFrozen: msg_cause = "grab frozen"; break;
default: msg_cause = "unknown failure"; break;
}
sprintf(msg, "%s: %s.", msg_prefix, msg_cause);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
msg);
}
/*
* Sets the proxy mode source window - the source window which the drag
* notifications from an XEmbed client should be forwarded to.
* If the window is not None and there is a drag operation in progress,
* throws InvalidDnDOperationException and doesn't change
* proxy_mode_source_window.
* The caller mush hold AWT_LOCK.
*/
void
set_proxy_mode_source_window(Window window) {
if (window != None && dnd_in_progress) {
JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Drag and drop is already in progress.");
return;
}
proxy_mode_source_window = window;
}
/*
* Checks if the event is a drag notification from an XEmbed client.
* If it is, forwards this event back to the current source and returns True.
* Otherwise, returns False.
* Currently only XDnD protocol notifications are recognized.
* The caller must hold AWT_LOCK.
*/
static Boolean
process_proxy_mode_event(XEvent* event) {
if (proxy_mode_source_window == None) {
return False;
}
if (event->type == ClientMessage) {
XClientMessageEvent* xclient = &event->xclient;
if (xclient->message_type == XA_XdndStatus ||
xclient->message_type == XA_XdndFinished) {
Window source = proxy_mode_source_window;
xclient->data.l[0] = xclient->window;
xclient->window = source;
XSendEvent(xclient->display, source, False, NoEventMask,
(XEvent*)xclient);
if (xclient->message_type == XA_XdndFinished) {
proxy_mode_source_window = None;
}
return True;
}
}
return False;
}
/*
* Class: sun_awt_motif_X11DragSourceContextPeer
* Method: startDrag
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_motif_X11DragSourceContextPeer_startDrag(JNIEnv *env,
jobject this,
jobject component,
jobject wpeer,
jobject transferable,
jobject trigger,
jobject cursor,
jint ctype,
jint actions,
jlongArray formats,
jobject formatMap) {
Time time_stamp = CurrentTime;
Cursor xcursor = None;
Window root_window = None;
Atom* targets = NULL;
jsize num_targets = 0;
AWT_LOCK();
if (dnd_in_progress) {
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Drag and drop is already in progress.");
AWT_UNLOCK();
return;
}
if (proxy_mode_source_window != None) {
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Proxy drag is in progress.");
AWT_UNLOCK();
return;
}
if (!awt_dnd_init(awt_display)) {
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"DnD subsystem initialization failed.");
AWT_UNLOCK();
return;
}
if (!JNU_IsNull(env, cursor)) {
xcursor = getCursor(env, cursor);
if (xcursor == None) {
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Invalid drag cursor");
AWT_UNLOCK();
}
}
/* Determine the root window for the drag operation. */
{
struct FrameData* wdata = (struct FrameData*)
JNU_GetLongFieldAsPtr(env, wpeer, mComponentPeerIDs.pData);
if (wdata == NULL) {
JNU_ThrowNullPointerException(env, "Null component data");
AWT_UNLOCK();
return;
}
if (wdata->winData.shell == NULL) {
JNU_ThrowNullPointerException(env, "Null shell widget");
AWT_UNLOCK();
return;
}
root_window = RootWindowOfScreen(XtScreen(wdata->winData.shell));
if (root_window == None) {
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot get the root window for the drag operation.");
AWT_UNLOCK();
return;
}
}
time_stamp = get_latest_time_stamp();
/* Extract the targets from java array. */
{
targets = NULL;
num_targets = (*env)->GetArrayLength(env, formats);
/*
* In debug build GetLongArrayElements aborts with assertion on an empty
* array.
*/
if (num_targets > 0) {
jboolean isCopy = JNI_TRUE;
jlong* java_targets = (*env)->GetLongArrayElements(env, formats,
&isCopy);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
AWT_UNLOCK();
return;
}
if (java_targets != NULL) {
targets = (Atom*)malloc(num_targets * sizeof(Atom));
if (targets != NULL) {
#ifdef _LP64
memcpy(targets, java_targets, num_targets * sizeof(Atom));
#else
jsize i;
for (i = 0; i < num_targets; i++) {
targets[i] = (Atom)java_targets[i];
}
#endif
}
(*env)->ReleaseLongArrayElements(env, formats, java_targets,
JNI_ABORT);
}
}
if (targets == NULL) {
num_targets = 0;
}
}
/* Write the XDnD initiator info on the awt_root_shell. */
{
unsigned char ret;
Atom action_atoms[3];
unsigned int action_count = 0;
if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) {
action_atoms[action_count] = XA_XdndActionCopy;
action_count++;
}
if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) {
action_atoms[action_count] = XA_XdndActionMove;
action_count++;
}
if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) {
action_atoms[action_count] = XA_XdndActionLink;
action_count++;
}
ret = checked_XChangeProperty(awt_display, awt_dnd_ds_get_source_window(),
XA_XdndActionList, XA_ATOM, 32,
PropModeReplace, (unsigned char*)action_atoms,
action_count * sizeof(Atom));
if (ret != Success) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot write XdndActionList property");
AWT_UNLOCK();
return;
}
ret = checked_XChangeProperty(awt_display, awt_dnd_ds_get_source_window(),
XA_XdndTypeList, XA_ATOM, 32,
PropModeReplace, (unsigned char*)targets,
num_targets);
if (ret != Success) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot write XdndTypeList property");
AWT_UNLOCK();
return;
}
}
/* Write the Motif DnD initiator info on the awt_root_shell. */
{
InitiatorInfo info;
unsigned char ret;
int target_list_index =
get_index_for_target_list(awt_display, targets, num_targets);
if (target_list_index == -1) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot determine the target list index.");
AWT_UNLOCK();
return;
}
info.byte_order = MOTIF_BYTE_ORDER;
info.protocol_version = MOTIF_DND_PROTOCOL_VERSION;
info.index = target_list_index;
info.selection_atom = _XA_MOTIF_ATOM_0;
ret = checked_XChangeProperty(awt_display, awt_dnd_ds_get_source_window(),
_XA_MOTIF_ATOM_0,
_XA_MOTIF_DRAG_INITIATOR_INFO, 8,
PropModeReplace, (unsigned char*)&info,
sizeof(InitiatorInfo));
if (ret != Success) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot write the Motif DnD initiator info");
AWT_UNLOCK();
return;
}
}
/* Acquire XDnD selection ownership. */
if (XtOwnSelection(awt_root_shell, XA_XdndSelection, time_stamp,
awt_convertData, NULL, NULL) != True) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot acquire XdndSelection ownership.");
AWT_UNLOCK();
return;
}
/* Acquire Motif DnD selection ownership. */
if (XtOwnSelection(awt_root_shell, _XA_MOTIF_ATOM_0, time_stamp,
motif_convert_proc, NULL, NULL) != True) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot acquire Motif DnD selection ownership.");
AWT_UNLOCK();
return;
}
/*
* Store the information needed to convert data for both selections
* in awt_convertDataContext.
*/
{
if (!set_convert_data_context(env, awt_display, XA_XdndSelection,
component, transferable, formatMap,
formats)) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot save context for XDnD selection data conversion.");
AWT_UNLOCK();
return;
}
if (!set_convert_data_context(env, awt_display, _XA_MOTIF_ATOM_0,
component, transferable, formatMap,
formats)) {
cleanup_drag(awt_display, time_stamp);
JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
"Cannot save context for Motif DnD selection data conversion.");
AWT_UNLOCK();
return;
}
}
/* Install X grabs. */
{
XWindowAttributes xwa;
int ret;
XGetWindowAttributes(awt_display, root_window, &xwa);
your_root_event_mask = xwa.your_event_mask;
XSelectInput(awt_display, root_window,
your_root_event_mask | ROOT_EVENT_MASK);
ret = XGrabPointer(awt_display,
root_window,
False,
GRAB_EVENT_MASK,
GrabModeAsync,
GrabModeAsync,
None,
xcursor,
time_stamp);
if (ret != GrabSuccess) {
cleanup_drag(awt_display, time_stamp);
throw_grab_failure_exception(env, ret, "Cannot grab pointer");
AWT_UNLOCK();
return;
}
ret = XGrabKeyboard(awt_display,
root_window,
False,
GrabModeAsync,
GrabModeAsync,
time_stamp);
if (ret != GrabSuccess) {
cleanup_drag(awt_display, time_stamp);
throw_grab_failure_exception(env, ret, "Cannot grab keyboard");
AWT_UNLOCK();
return;
}
}
/* Update the global state. */
source_peer = (*env)->NewGlobalRef(env, this);
dnd_in_progress = True;
drag_in_progress = True;
data_types = targets;
data_types_count = num_targets;
source_actions = actions;
drag_root_window = root_window;
AWT_UNLOCK();
}
/*
* Class: sun_awt_motif_X11DragSourceContextPeer
* Method: setNativeCursor
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_motif_X11DragSourceContextPeer_setNativeCursor(JNIEnv *env,
jobject this,
jlong nativeCtxt,
jobject cursor,
jint type) {
if (JNU_IsNull(env, cursor)) {
return;
}
XChangeActivePointerGrab(awt_display,
GRAB_EVENT_MASK,
getCursor(env, cursor),
CurrentTime);
}