/* * 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); }