| /* |
| * 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" |
| |
| #include "jlong.h" |
| |
| #include "awt_DataTransferer.h" |
| #include "awt_MToolkit.h" |
| |
| #include "java_awt_dnd_DnDConstants.h" |
| #include "java_awt_event_MouseEvent.h" |
| |
| #include "sun_awt_motif_MComponentPeer.h" |
| #include "awt_xembed.h" |
| |
| #define DT_INITIAL_STATE 0 |
| #define DT_ENTERED_STATE 1 |
| #define DT_OVER_STATE 2 |
| |
| extern struct ComponentIDs componentIDs; |
| extern struct MComponentPeerIDs mComponentPeerIDs; |
| |
| /**************************** XEmbed server DnD support ***********************/ |
| extern void |
| set_xembed_drop_target(JNIEnv* env, jobject server); |
| extern void |
| remove_xembed_drop_target(JNIEnv* env, jobject server); |
| extern Boolean |
| is_xembed_client(Window window); |
| |
| DECLARE_JAVA_CLASS(MEmbedCanvasPeerClass, "sun/awt/motif/MEmbedCanvasPeer"); |
| /******************************************************************************/ |
| |
| typedef enum { |
| EventSuccess, /* Event is successfully processed. */ |
| EventFailure /* Failed to process the event. */ |
| } EventStatus; |
| |
| typedef enum { |
| EnterEvent, /* XdndEnter, TOP_LEVEL_ENTER */ |
| MotionEvent, /* XdndPosition, DRAG_MOTION, OPERATION_CHANGED */ |
| LeaveEvent, /* XdndLeave, TOP_LEVEL_LEAVE */ |
| DropEvent, /* XdndDrop, DROP_START */ |
| UnknownEvent |
| } EventType; |
| |
| static Protocol source_protocol = NO_PROTOCOL; |
| static unsigned int source_protocol_version = 0; |
| static Window source_window = None; |
| static Atom source_atom = None; |
| static long source_window_mask = None; |
| static jint source_actions = java_awt_dnd_DnDConstants_ACTION_NONE; |
| /* |
| * According to XDnD protocol, XdndActionList is optional. |
| * In case if XdndActionList is not set on the source, the list of drop actions |
| * supported by the source is constructed as follows: |
| * - "copy" is always included; |
| * - "move" is included if at least one XdndPosition message received |
| * after the latest XdndEnter passed XdndActionMove in data.l[4]; |
| * - "link" is included if at least one XdndPosition message received |
| * after the latest XdndEnter passed XdndActionLink in data.l[4]. |
| * We use a boolean flag to signal that we are building the list of drop actions |
| * supported by the source. |
| */ |
| static Boolean track_source_actions = False; |
| static jint user_action = java_awt_dnd_DnDConstants_ACTION_NONE; |
| static jlongArray source_data_types = NULL; |
| static Atom* source_data_types_native = NULL; |
| static unsigned int source_data_types_count = 0; |
| static int source_x = 0; |
| static int source_y = 0; |
| static jobject target_component = NULL; |
| /* |
| * The Motif DnD protocol prescribes that DROP_START message should always be |
| * preceeded with TOP_LEVEL_LEAVE message. We need to cleanup on TOP_LEVEL_LEAVE |
| * message, but DROP_START wouldn't be processed properly. |
| * To resolve this issue we postpone cleanup using a boolean flag this flag is |
| * set when we receive the TOP_LEVEL_LEAVE message and cleared when the next |
| * client message arrives if that message is not DROP_START. If that message is |
| * a DROP_START message, the flag is cleared after the DROP_START is processed. |
| */ |
| static Boolean motif_top_level_leave_postponed = False; |
| /* |
| * We store a postponed TOP_LEVEL_LEAVE message here. |
| */ |
| static XClientMessageEvent motif_top_level_leave_postponed_event; |
| |
| /* Forward declarations */ |
| static Window get_root_for_window(Window window); |
| static Window get_outer_canvas_for_window(Window window); |
| static Boolean register_drop_site(Widget outer_canvas, XtPointer componentRef); |
| static Boolean is_xdnd_drag_message_type(unsigned long message_type); |
| static Boolean register_xdnd_drop_site(Display* dpy, Window toplevel, |
| Window window); |
| |
| /**************************** JNI stuff ***************************************/ |
| |
| DECLARE_JAVA_CLASS(dtcp_clazz, "sun/awt/motif/X11DropTargetContextPeer") |
| |
| static void |
| dt_postDropTargetEvent(JNIEnv* env, jobject component, int x, int y, |
| jint dropAction, jint event_id, |
| XClientMessageEvent* event) { |
| DECLARE_STATIC_VOID_JAVA_METHOD(dtcp_postDropTargetEventToPeer, dtcp_clazz, |
| "postDropTargetEventToPeer", |
| "(Ljava/awt/Component;IIII[JJI)V"); |
| |
| { |
| void* copy = NULL; |
| |
| if (event != NULL) { |
| /* |
| * For XDnD messages we append the information from the latest |
| * XdndEnter to the context. It is done to be able to reconstruct |
| * XdndEnter for an XEmbed client. |
| */ |
| Boolean isXDnDMessage = |
| is_xdnd_drag_message_type(event->message_type); |
| |
| if (isXDnDMessage) { |
| copy = malloc(sizeof(XClientMessageEvent) + |
| 4 * sizeof(long)); |
| } else { |
| copy = malloc(sizeof(XClientMessageEvent)); |
| } |
| |
| if (copy == NULL) { |
| DTRACE_PRINTLN2("%s:%d malloc failed.", __FILE__, __LINE__); |
| return; |
| } |
| |
| memcpy(copy, event, sizeof(XClientMessageEvent)); |
| |
| if (isXDnDMessage) { |
| size_t msgSize = sizeof(XClientMessageEvent); |
| long data1 = source_protocol_version << XDND_PROTOCOL_SHIFT; |
| long * appended_data; |
| if (source_data_types_native != NULL && |
| source_data_types_count > 3) { |
| |
| data1 |= XDND_DATA_TYPES_BIT; |
| } |
| |
| appended_data = (long*)((char*)copy + msgSize); |
| appended_data[0] = data1; |
| appended_data[1] = source_data_types_count > 0 ? |
| source_data_types_native[0] : 0; |
| appended_data[2] = source_data_types_count > 1 ? |
| source_data_types_native[1] : 0; |
| appended_data[3] = source_data_types_count > 2 ? |
| source_data_types_native[2] : 0; |
| } |
| } |
| |
| DASSERT(!JNU_IsNull(env, component)); |
| |
| (*env)->CallStaticVoidMethod(env, clazz, dtcp_postDropTargetEventToPeer, |
| component, x, y, dropAction, |
| source_actions, source_data_types, |
| ptr_to_jlong(copy), event_id); |
| } |
| } |
| |
| /******************************************************************************/ |
| |
| /********************* Embedded drop site list support ************************/ |
| |
| struct EmbeddedDropSiteListEntryRec; |
| |
| typedef struct EmbeddedDropSiteListEntryRec EmbeddedDropSiteListEntry; |
| |
| struct EmbeddedDropSiteListEntryRec { |
| Window toplevel; |
| Window root; |
| /* |
| * We select for PropertyNotify events on the toplevel, so we need to |
| * restore the event mask when we are done with this toplevel. |
| */ |
| long event_mask; |
| unsigned int embedded_sites_count; |
| Window* embedded_sites; |
| EmbeddedDropSiteListEntry* next; |
| }; |
| |
| static EmbeddedDropSiteListEntry* embedded_drop_site_list = NULL; |
| |
| struct EmbeddedDropSiteProtocolListEntryRec; |
| |
| typedef struct EmbeddedDropSiteProtocolListEntryRec EmbeddedDropSiteProtocolListEntry; |
| |
| struct EmbeddedDropSiteProtocolListEntryRec { |
| Window window; |
| Window proxy; |
| /* |
| * We override the XdndAware property on the toplevel, so we should keep its |
| * original contents - the XDnD protocol version supported by the browser. |
| * This is needed to adjust XDnD messages forwarded to the browser. |
| */ |
| unsigned int protocol_version; |
| /* True if the toplevel was already registered as a drag receiver and |
| we just changed the proxy. False, otherwise */ |
| Boolean overriden; |
| EmbeddedDropSiteProtocolListEntry* next; |
| }; |
| |
| static EmbeddedDropSiteProtocolListEntry* embedded_motif_protocol_list = NULL; |
| static EmbeddedDropSiteProtocolListEntry* embedded_xdnd_protocol_list = NULL; |
| |
| typedef enum { |
| RegFailure, /* Proxy registration failed */ |
| RegSuccess, /* The new drop site is registered with the new proxy */ |
| RegOverride, /* The new proxy is set for the existing drop site */ |
| RegAlreadyRegistered /* This proxy is already set for this drop site */ |
| } ProxyRegistrationStatus; |
| |
| /* Forward declarations. */ |
| static EmbeddedDropSiteProtocolListEntry* |
| get_xdnd_protocol_entry_for_toplevel(Window toplevel); |
| static EmbeddedDropSiteProtocolListEntry* |
| get_motif_protocol_entry_for_toplevel(Window toplevel); |
| static void remove_xdnd_protocol_entry_for_toplevel(Window toplevel); |
| static void remove_motif_protocol_entry_for_toplevel(Window toplevel); |
| |
| /* |
| * Registers the toplevel as a Motif drag receiver if it is not registered yet, |
| * sets the specified new_proxy for it and returns the previous proxy in old_proxy. |
| * Does nothing if the new_proxy is already set as a proxy for this toplevel. |
| * Returns the completion status. |
| */ |
| static ProxyRegistrationStatus |
| set_motif_proxy(Display* dpy, Window toplevel, Window new_proxy, Window *old_proxy) { |
| Boolean override = False; |
| |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char* data; |
| unsigned char ret; |
| |
| DASSERT(old_proxy != NULL); |
| |
| *old_proxy = None; |
| |
| data = NULL; |
| ret = checked_XGetWindowProperty(dpy, toplevel, |
| _XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF, |
| False, AnyPropertyType, &type, &format, |
| &nitems, &after, &data); |
| |
| /* Check if toplevel is a valid window. */ |
| if (ret != Success) { |
| return RegFailure; |
| } |
| |
| if (ret == Success && data != NULL && type != None && format == 8 |
| && nitems >= MOTIF_RECEIVER_INFO_SIZE) { |
| unsigned char byte_order = read_card8((char*)data, 0); |
| void* p = (char*)data + 4; |
| |
| /* Browser and plugin have different byte orders - report failure for now. */ |
| if (MOTIF_BYTE_ORDER != byte_order) { |
| XFree(data); |
| return RegFailure; |
| } |
| |
| *old_proxy = read_card32((char*)data, 4, byte_order); |
| |
| /* If the proxy is already set to the specified window - return. */ |
| if (*old_proxy == new_proxy) { |
| XFree(data); |
| return RegAlreadyRegistered; |
| } |
| |
| /* replace the proxy window */ |
| write_card32(&p, new_proxy); |
| |
| override = True; |
| } else { |
| void* p; |
| |
| if (ret == Success) { |
| XFree(data); |
| data = NULL; |
| } |
| |
| data = malloc(MOTIF_RECEIVER_INFO_SIZE); |
| |
| if (data == NULL) { |
| DTRACE_PRINTLN2("%s:%d malloc failed.", __FILE__, __LINE__); |
| return RegFailure; |
| } |
| |
| p = data; |
| |
| write_card8(&p, MOTIF_BYTE_ORDER); |
| write_card8(&p, MOTIF_DND_PROTOCOL_VERSION); /* protocol version */ |
| write_card8(&p, MOTIF_DYNAMIC_STYLE); /* protocol style */ |
| write_card8(&p, 0); /* pad */ |
| write_card32(&p, new_proxy); /* proxy window */ |
| write_card16(&p, 0); /* num_drop_sites */ |
| write_card16(&p, 0); /* pad */ |
| write_card32(&p, MOTIF_RECEIVER_INFO_SIZE); |
| } |
| |
| ret = checked_XChangeProperty(dpy, toplevel, |
| _XA_MOTIF_DRAG_RECEIVER_INFO, |
| _XA_MOTIF_DRAG_RECEIVER_INFO, 8, |
| PropModeReplace, (unsigned char*)data, |
| MOTIF_RECEIVER_INFO_SIZE); |
| |
| if (data != NULL) { |
| XFree(data); |
| data = NULL; |
| } |
| |
| if (ret == Success) { |
| if (override) { |
| return RegOverride; |
| } else { |
| return RegSuccess; |
| } |
| } else { |
| return RegFailure; |
| } |
| } |
| |
| /* |
| * Registers the toplevel as a XDnD drag receiver if it is not registered yet, |
| * sets the specified new_proxy for it and returns the previous proxy in |
| * old_proxy and the original XDnD protocol version in old_version. |
| * Does nothing if the new_proxy is already set as a proxy for this toplevel. |
| * Returns the completion status. |
| */ |
| static ProxyRegistrationStatus |
| set_xdnd_proxy(Display* dpy, Window toplevel, Window new_proxy, |
| Window* old_proxy, unsigned int* old_version) { |
| Atom version_atom = XDND_PROTOCOL_VERSION; |
| Window xdnd_proxy = None; |
| Boolean override = False; |
| |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char* data; |
| unsigned char ret; |
| |
| DASSERT(old_proxy != NULL); |
| |
| *old_proxy = None; |
| |
| data = NULL; |
| ret = checked_XGetWindowProperty(dpy, toplevel, XA_XdndAware, 0, 1, |
| False, AnyPropertyType, &type, &format, |
| &nitems, &after, &data); |
| |
| if (ret != Success) { |
| return RegFailure; |
| } |
| |
| if (ret == Success && data != NULL && type == XA_ATOM) { |
| unsigned int protocol_version = *((unsigned int*)data); |
| |
| override = True; |
| *old_version = protocol_version; |
| |
| /* XdndProxy is not supported for prior to XDnD version 4 */ |
| if (protocol_version >= 4) { |
| int status; |
| |
| XFree(data); |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, toplevel, XA_XdndProxy, 0, 1, |
| False, XA_WINDOW, &type, &format, |
| &nitems, &after, &data); |
| |
| if (status == Success && data != NULL && type == XA_WINDOW) { |
| xdnd_proxy = *((Window*)data); |
| |
| if (xdnd_proxy != None) { |
| XFree(data); |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndProxy, |
| 0, 1, False, XA_WINDOW, &type, |
| &format, &nitems, &after, &data); |
| |
| if (status != Success || data == NULL || type != XA_WINDOW || |
| *((Window*)data) != xdnd_proxy) { |
| /* Ignore invalid proxy. */ |
| xdnd_proxy = None; |
| } |
| } |
| |
| if (xdnd_proxy != None) { |
| XFree(data); |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndAware, |
| 0, 1, False, AnyPropertyType, |
| &type, &format, &nitems, &after, |
| &data); |
| |
| if (status == Success && data != NULL && type == XA_ATOM) { |
| unsigned int proxy_version = *((unsigned int*)data); |
| |
| if (proxy_version != protocol_version) { |
| /* Ignore invalid proxy. */ |
| xdnd_proxy = None; |
| } |
| } else { |
| /* Ignore invalid proxy. */ |
| xdnd_proxy = None; |
| } |
| } |
| } |
| |
| *old_proxy = xdnd_proxy; |
| } |
| } |
| |
| XFree(data); |
| |
| /* If the proxy is already set to the specified window - return. */ |
| if (xdnd_proxy == new_proxy) { |
| return RegAlreadyRegistered; |
| } |
| |
| /* The proxy window must have the XdndAware set, as XDnD protocol prescribes |
| to check the proxy window for XdndAware. */ |
| ret = checked_XChangeProperty(dpy, new_proxy, XA_XdndAware, XA_ATOM, 32, |
| PropModeReplace, |
| (unsigned char*)&version_atom, 1); |
| |
| if (ret != Success) { |
| return RegFailure; |
| } |
| |
| /* The proxy window must have the XdndProxy set to point to itself. */ |
| ret = checked_XChangeProperty(dpy, new_proxy, XA_XdndProxy, XA_WINDOW, 32, |
| PropModeReplace, |
| (unsigned char*)&new_proxy, 1); |
| |
| if (ret != Success) { |
| return RegFailure; |
| } |
| |
| ret = checked_XChangeProperty(dpy, toplevel, XA_XdndAware, XA_ATOM, 32, |
| PropModeReplace, |
| (unsigned char*)&version_atom, 1); |
| |
| if (ret != Success) { |
| return RegFailure; |
| } |
| |
| ret = checked_XChangeProperty(dpy, toplevel, XA_XdndProxy, XA_WINDOW, 32, |
| PropModeReplace, |
| (unsigned char*)&new_proxy, 1); |
| |
| if (ret == Success) { |
| if (override) { |
| return RegOverride; |
| } else { |
| return RegSuccess; |
| } |
| } else { |
| return RegFailure; |
| } |
| } |
| |
| /* |
| * 'toplevel' is the browser toplevel window. To register a drop site on the |
| * plugin window we set the proxy for the browser toplevel window to point to |
| * the awt_root_shell window. |
| * |
| * We assume that only one JVM per browser instance is possible. This |
| * assumption is true with the current plugin implementation - it creates a |
| * single JVM for all plugin instances created by the given plugin factory. |
| * |
| * When a client message event for the browser toplevel window is received, we |
| * will iterate over drop sites registered with this toplevel and determine if |
| * the mouse pointer is currently over one of them (there could be several |
| * plugin windows in one browser window - for example if an HTML page contains |
| * frames and several frames contain a plugin object). |
| * |
| * If the pointer is not over any of the plugin drop sites the client message |
| * will be resent to the browser, otherwise it will be processed normally. |
| */ |
| static EmbeddedDropSiteListEntry* |
| awt_dnd_dt_init_proxy(Display* dpy, Window root, Window toplevel, Window window) { |
| Window awt_root_window = get_awt_root_window(); |
| Window motif_proxy = None; |
| Boolean motif_override = False; |
| unsigned long event_mask = 0; |
| |
| if (awt_root_window == None) { |
| return NULL; |
| } |
| |
| /* Grab server, since we are working with the window that belongs to |
| another client. REMIND: ungrab when done!!! */ |
| XGrabServer(dpy); |
| |
| { |
| ProxyRegistrationStatus motif_status = RegFailure; |
| |
| motif_status = set_motif_proxy(dpy, toplevel, awt_root_window, &motif_proxy); |
| |
| switch (motif_status) { |
| case RegFailure: |
| case RegAlreadyRegistered: |
| XUngrabServer(dpy); |
| /* Workaround for bug 5039226 */ |
| XSync(dpy, False); |
| return NULL; |
| case RegOverride: |
| motif_override = True; |
| break; |
| case RegSuccess: |
| motif_override = False; |
| break; |
| default: |
| DASSERT(False); |
| } |
| |
| |
| } |
| |
| { |
| XWindowAttributes xwa; |
| XGetWindowAttributes(dpy, toplevel, &xwa); |
| event_mask = xwa.your_event_mask; |
| if ((event_mask & PropertyChangeMask) == 0) { |
| XSelectInput(dpy, toplevel, event_mask | PropertyChangeMask); |
| } |
| } |
| |
| XUngrabServer(dpy); |
| /* Workaround for bug 5039226 */ |
| XSync(dpy, False); |
| |
| /* Add protocol specific entries for the toplevel. */ |
| { |
| EmbeddedDropSiteProtocolListEntry* motif_entry = NULL; |
| |
| motif_entry = malloc(sizeof(EmbeddedDropSiteProtocolListEntry)); |
| |
| if (motif_entry == NULL) { |
| return NULL; |
| } |
| |
| motif_entry->window = toplevel; |
| motif_entry->proxy = motif_proxy; |
| motif_entry->protocol_version = 0; |
| motif_entry->overriden = motif_override; |
| motif_entry->next = embedded_motif_protocol_list; |
| embedded_motif_protocol_list = motif_entry; |
| } |
| |
| { |
| EmbeddedDropSiteListEntry* entry = NULL; |
| Window* sites = NULL; |
| |
| entry = malloc(sizeof(EmbeddedDropSiteListEntry)); |
| |
| if (entry == NULL) { |
| return NULL; |
| } |
| |
| sites = malloc(sizeof(Window)); |
| |
| if (sites == NULL) { |
| free(entry); |
| return NULL; |
| } |
| |
| sites[0] = window; |
| |
| entry->toplevel = toplevel; |
| entry->root = root; |
| entry->event_mask = event_mask; |
| entry->embedded_sites_count = 1; |
| entry->embedded_sites = sites; |
| entry->next = NULL; |
| |
| return entry; |
| } |
| } |
| |
| static void |
| register_xdnd_embedder(Display* dpy, EmbeddedDropSiteListEntry* entry, long window) { |
| Window awt_root_window = get_awt_root_window(); |
| Window toplevel = entry->toplevel; |
| Window xdnd_proxy = None; |
| unsigned int xdnd_protocol_version = 0; |
| Boolean xdnd_override = False; |
| Boolean register_xdnd = True; |
| Boolean motif_overriden = False; |
| |
| EmbeddedDropSiteProtocolListEntry* motif_entry = embedded_motif_protocol_list; |
| while (motif_entry != NULL) { |
| if (motif_entry->window == toplevel) { |
| motif_overriden = motif_entry->overriden; |
| break; |
| } |
| motif_entry = motif_entry->next; |
| } |
| |
| /* |
| * First check if the window is an XEmbed client. |
| * In this case we don't have to setup a proxy on the toplevel, |
| * instead we register the XDnD drop site on the embedded window. |
| */ |
| if (isXEmbedActiveByWindow(window)) { |
| register_xdnd_drop_site(dpy, toplevel, window); |
| return; |
| } |
| |
| /* |
| * By default, we register a drop site that supports both dnd |
| * protocols. This approach is not appropriate in plugin |
| * scenario if the browser doesn't support XDnD. If we forcibly set |
| * XdndAware on the browser toplevel, any drag source that supports both |
| * protocols and prefers XDnD will be unable to drop anything on the |
| * browser. |
| * The solution for this problem is not to register XDnD drop site |
| * if the browser supports only Motif DnD. |
| */ |
| if (motif_overriden) { |
| int status; |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char* data; |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, toplevel, XA_XdndAware, 0, 1, |
| False, AnyPropertyType, &type, &format, |
| &nitems, &after, &data); |
| |
| XFree(data); |
| data = NULL; |
| |
| if (type != XA_ATOM) { |
| register_xdnd = False; |
| } |
| } |
| |
| if (register_xdnd) { |
| ProxyRegistrationStatus xdnd_status; |
| /* Grab server, since we are working with the window that belongs to |
| another client. REMIND: ungrab when done!!! */ |
| XGrabServer(dpy); |
| |
| xdnd_status = |
| set_xdnd_proxy(dpy, toplevel, awt_root_window, &xdnd_proxy, |
| &xdnd_protocol_version); |
| |
| XUngrabServer(dpy); |
| |
| switch (xdnd_status) { |
| case RegFailure: |
| case RegAlreadyRegistered: |
| return; |
| case RegOverride: |
| xdnd_override = True; |
| break; |
| case RegSuccess: |
| xdnd_override = False; |
| break; |
| default: |
| DASSERT(False); |
| } |
| |
| { |
| EmbeddedDropSiteProtocolListEntry* xdnd_entry = NULL; |
| |
| xdnd_entry = malloc(sizeof(EmbeddedDropSiteProtocolListEntry)); |
| |
| if (xdnd_entry == NULL) { |
| return; |
| } |
| |
| xdnd_entry->window = toplevel; |
| xdnd_entry->proxy = xdnd_proxy; |
| xdnd_entry->protocol_version = xdnd_protocol_version; |
| xdnd_entry->overriden = xdnd_override; |
| xdnd_entry->next = embedded_xdnd_protocol_list; |
| embedded_xdnd_protocol_list = xdnd_entry; |
| } |
| } |
| } |
| |
| /* |
| * If embedded_drop_site_list already contains an entry with the specified |
| * 'toplevel', the method registers the specified 'window' as an embedded drop |
| * site for this 'toplevel' and returns True. |
| * Otherwise, it checks if the 'toplevel' is a registered drop site for adds |
| * (window, component) pair to the list and returns True |
| * if completes successfully. |
| */ |
| static Boolean |
| add_to_embedded_drop_site_list(Display* dpy, Window root, Window toplevel, |
| Window window) { |
| EmbeddedDropSiteListEntry* entry = embedded_drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->toplevel == toplevel) { |
| void* p = realloc(entry->embedded_sites, |
| sizeof(Window) * |
| (entry->embedded_sites_count + 1)); |
| if (p == NULL) { |
| return False; |
| } |
| entry->embedded_sites = p; |
| entry->embedded_sites[entry->embedded_sites_count++] = window; |
| |
| register_xdnd_embedder(dpy, entry, window); |
| |
| return True; |
| } |
| entry = entry->next; |
| } |
| |
| entry = awt_dnd_dt_init_proxy(dpy, root, toplevel, window); |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| register_xdnd_embedder(dpy, entry, window); |
| |
| entry->next = embedded_drop_site_list; |
| embedded_drop_site_list = entry; |
| |
| return True; |
| } |
| |
| /* |
| * Removes the window from the list of embedded drop sites for the toplevel. |
| * Returns True if the window was successfully removed, False otherwise. |
| */ |
| static Boolean |
| remove_from_embedded_drop_site_list(Display* dpy, Window toplevel, Window window) { |
| EmbeddedDropSiteListEntry* entry = embedded_drop_site_list; |
| EmbeddedDropSiteListEntry* prev = NULL; |
| |
| while (entry != NULL) { |
| if (entry->toplevel == toplevel) { |
| unsigned int idx; |
| |
| for (idx = 0; idx < entry->embedded_sites_count; idx++) { |
| if (entry->embedded_sites[idx] == window) { |
| int tail = entry->embedded_sites_count - idx - 1; |
| if (tail > 0) { |
| memmove(entry->embedded_sites + idx, |
| entry->embedded_sites + idx + 1, |
| tail * sizeof(Window)); |
| } |
| entry->embedded_sites_count--; |
| |
| /* If the list of embedded drop sites for this toplevel |
| becomes empty - restore the original proxies and remove |
| the entry. */ |
| if (entry->embedded_sites_count == 0) { |
| Widget w = XtWindowToWidget(dpy, toplevel); |
| |
| if (w != NULL) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| Widget copy = w; |
| jobject peer = findPeer(&w); |
| |
| if (!JNU_IsNull(env, peer) && |
| (*env)->IsInstanceOf(env, peer, |
| get_MEmbedCanvasPeerClass(env)) == JNI_TRUE) { |
| remove_xembed_drop_target(env, peer); |
| } |
| } else { |
| EmbeddedDropSiteProtocolListEntry* xdnd_entry = |
| get_xdnd_protocol_entry_for_toplevel(toplevel); |
| EmbeddedDropSiteProtocolListEntry* motif_entry = |
| get_motif_protocol_entry_for_toplevel(toplevel); |
| |
| if (xdnd_entry != NULL) { |
| if (xdnd_entry->overriden == True) { |
| XChangeProperty(dpy, toplevel, XA_XdndAware, |
| XA_ATOM, 32, |
| PropModeReplace, |
| (unsigned char*)&xdnd_entry->protocol_version, |
| 1); |
| |
| XChangeProperty(dpy, toplevel, XA_XdndProxy, |
| XA_WINDOW, 32, |
| PropModeReplace, |
| (unsigned char*)&xdnd_entry->proxy, 1); |
| } else { |
| XDeleteProperty(dpy, toplevel, XA_XdndAware); |
| XDeleteProperty(dpy, toplevel, XA_XdndProxy); |
| } |
| remove_xdnd_protocol_entry_for_toplevel(toplevel); |
| } |
| |
| if (motif_entry != NULL) { |
| if (motif_entry->overriden == True) { |
| /* Request status */ |
| int status; |
| |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char* data; |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, toplevel, |
| _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); |
| void* p = (char*)data + 4; |
| |
| DASSERT(MOTIF_BYTE_ORDER == byte_order); |
| |
| if (MOTIF_BYTE_ORDER == byte_order) { |
| /* restore the original proxy window */ |
| write_card32(&p, motif_entry->proxy); |
| |
| XChangeProperty(dpy, toplevel, |
| _XA_MOTIF_DRAG_RECEIVER_INFO, |
| _XA_MOTIF_DRAG_RECEIVER_INFO, 8, |
| PropModeReplace, |
| (unsigned char*)data, |
| MOTIF_RECEIVER_INFO_SIZE); |
| } |
| } |
| |
| if (status == Success) { |
| XFree(data); |
| } |
| } else { |
| XDeleteProperty(dpy, toplevel, _XA_MOTIF_DRAG_RECEIVER_INFO); |
| } |
| |
| remove_motif_protocol_entry_for_toplevel(toplevel); |
| } |
| |
| if ((entry->event_mask & PropertyChangeMask) == 0) { |
| XSelectInput(dpy, toplevel, entry->event_mask); |
| } |
| } |
| |
| if (prev == NULL) { |
| embedded_drop_site_list = entry->next; |
| } else { |
| prev->next = entry->next; |
| } |
| |
| free(entry); |
| } |
| return True; |
| } |
| } |
| return False; |
| } |
| prev = entry; |
| entry = entry->next; |
| } |
| return False; |
| } |
| |
| static EmbeddedDropSiteListEntry* |
| get_entry_for_toplevel(Window toplevel) { |
| EmbeddedDropSiteListEntry* entry = embedded_drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->toplevel == toplevel) { |
| return entry; |
| } |
| entry = entry->next; |
| } |
| return NULL; |
| } |
| |
| static EmbeddedDropSiteProtocolListEntry* |
| get_motif_protocol_entry_for_toplevel(Window toplevel) { |
| EmbeddedDropSiteProtocolListEntry* entry = embedded_motif_protocol_list; |
| |
| while (entry != NULL) { |
| if (entry->window == toplevel) { |
| return entry; |
| } |
| entry = entry->next; |
| } |
| return NULL; |
| } |
| |
| static EmbeddedDropSiteProtocolListEntry* |
| get_xdnd_protocol_entry_for_toplevel(Window toplevel) { |
| EmbeddedDropSiteProtocolListEntry* entry = embedded_xdnd_protocol_list; |
| |
| while (entry != NULL) { |
| if (entry->window == toplevel) { |
| return entry; |
| } |
| entry = entry->next; |
| } |
| return NULL; |
| } |
| |
| static void |
| remove_motif_protocol_entry_for_toplevel(Window toplevel) { |
| EmbeddedDropSiteProtocolListEntry* entry = embedded_motif_protocol_list; |
| EmbeddedDropSiteProtocolListEntry* prev_entry = NULL; |
| |
| while (entry != NULL) { |
| if (entry->window == toplevel) { |
| if (prev_entry != NULL) { |
| prev_entry->next = entry->next; |
| } else { |
| embedded_motif_protocol_list = entry->next; |
| } |
| free(entry); |
| } |
| entry = entry->next; |
| prev_entry = entry; |
| } |
| } |
| |
| static void |
| remove_xdnd_protocol_entry_for_toplevel(Window toplevel) { |
| EmbeddedDropSiteProtocolListEntry* entry = embedded_xdnd_protocol_list; |
| EmbeddedDropSiteProtocolListEntry* prev_entry = NULL; |
| |
| while (entry != NULL) { |
| if (entry->window == toplevel) { |
| if (prev_entry != NULL) { |
| prev_entry->next = entry->next; |
| } else { |
| embedded_xdnd_protocol_list = entry->next; |
| } |
| free(entry); |
| } |
| entry = entry->next; |
| } |
| } |
| |
| static Boolean |
| is_embedding_toplevel(Window toplevel) { |
| return get_entry_for_toplevel(toplevel) != NULL; |
| } |
| |
| static Window |
| get_embedded_window(Display* dpy, Window toplevel, int x, int y) { |
| EmbeddedDropSiteListEntry* entry = get_entry_for_toplevel(toplevel); |
| |
| if (entry != NULL) { |
| unsigned int idx; |
| |
| for (idx = 0; idx < entry->embedded_sites_count; idx++) { |
| Window site = entry->embedded_sites[idx]; |
| Window child = None; |
| int x_return, y_return; |
| |
| if (XTranslateCoordinates(dpy, entry->root, site, x, y, |
| &x_return, &y_return, &child)) { |
| if (x_return >= 0 && y_return >= 0) { |
| XWindowAttributes xwa; |
| XGetWindowAttributes(dpy, site, &xwa); |
| if (xwa.map_state != IsUnmapped && |
| x_return < xwa.width && y_return < xwa.height) { |
| return site; |
| } |
| } |
| } |
| } |
| } |
| |
| return None; |
| } |
| |
| /* |
| * If the toplevel is not an embedding toplevel does nothing and returns False. |
| * Otherwise, sets xdnd_proxy for the specified toplevel to the 'proxy_window', |
| * xdnd_protocol_version to 'version', xdnd_override to 'override', returns True. |
| */ |
| static Boolean |
| set_xdnd_proxy_for_toplevel(Window toplevel, Window proxy_window, |
| unsigned int version, Boolean override) { |
| EmbeddedDropSiteProtocolListEntry* entry = |
| get_xdnd_protocol_entry_for_toplevel(toplevel); |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| entry->proxy = proxy_window; |
| entry->protocol_version = version; |
| entry->overriden = override; |
| |
| return True; |
| } |
| |
| /* |
| * If the toplevel is not an embedding toplevel does nothing and returns False. |
| * Otherwise, sets motif_proxy for the specified toplevel to the proxy_window, |
| * motif_override to 'override' and returns True. |
| */ |
| static Boolean |
| set_motif_proxy_for_toplevel(Window toplevel, Window proxy_window, Boolean override) { |
| EmbeddedDropSiteProtocolListEntry* entry = |
| get_motif_protocol_entry_for_toplevel(toplevel); |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| entry->proxy = proxy_window; |
| entry->overriden = override; |
| |
| return True; |
| } |
| |
| /* |
| * Forwards a drag notification to the embedding toplevel modifying the event |
| * to match the protocol version supported by the toplevel. |
| * Returns True if the event is sent, False otherwise. |
| */ |
| static Boolean |
| forward_client_message_to_toplevel(Window toplevel, XClientMessageEvent* event) { |
| EmbeddedDropSiteProtocolListEntry* protocol_entry = NULL; |
| Window proxy = None; |
| |
| if (event->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) { |
| protocol_entry = get_motif_protocol_entry_for_toplevel(toplevel); |
| } else { |
| /* Assume XDnD */ |
| protocol_entry = get_xdnd_protocol_entry_for_toplevel(toplevel); |
| if (protocol_entry != NULL) { |
| /* Adjust the event to match the XDnD protocol version. */ |
| unsigned int version = protocol_entry->protocol_version; |
| if (event->message_type == XA_XdndEnter) { |
| unsigned int min_version = source_protocol_version < version ? |
| source_protocol_version : version; |
| event->data.l[1] = min_version << XDND_PROTOCOL_SHIFT; |
| event->data.l[1] |= source_data_types_count > 3 ? XDND_DATA_TYPES_BIT : 0; |
| } |
| } |
| } |
| |
| if (protocol_entry == NULL) { |
| return False; |
| } |
| |
| if (!protocol_entry->overriden) { |
| return False; |
| } |
| proxy = protocol_entry->proxy; |
| |
| if (proxy == None) { |
| proxy = toplevel; |
| } |
| |
| event->window = toplevel; |
| |
| XSendEvent(event->display, proxy, False, NoEventMask, (XEvent*)event); |
| |
| return True; |
| } |
| |
| /******************************************************************************/ |
| |
| /********************* Drop site list support *********************************/ |
| |
| struct DropSiteListEntryRec; |
| |
| typedef struct DropSiteListEntryRec DropSiteListEntry; |
| |
| struct DropSiteListEntryRec { |
| Window window; |
| Window root; |
| /* |
| * The closest to the root ancestor with WM_STATE property set. |
| * Normally toplevel == window. |
| * In plugin scenario toplevel is the browser toplevel window. |
| */ |
| Window toplevel; |
| /* |
| * Java top-level position is the outer canvas position, not the shell |
| * window position. We need to keep the outer canvas ID (and the root ID) to |
| * translate from mouse position root coordinates to the Java component |
| * coordinates. |
| */ |
| Window outer_canvas; |
| jobject component; |
| DropSiteListEntry* next; |
| }; |
| |
| static DropSiteListEntry* drop_site_list = NULL; |
| |
| /* |
| * If drop_site_list already contains an entry with the same window, |
| * does nothing and returns False. |
| * Otherwise, adds a new entry the list and returns True |
| * if completes successfully. |
| */ |
| static Boolean |
| add_to_drop_site_list(Window window, Window root, Window toplevel, |
| Window outer_canvas, jobject component) { |
| DropSiteListEntry* entry = drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->window == window) { |
| return False; |
| } |
| entry = entry->next; |
| } |
| |
| entry = malloc(sizeof(DropSiteListEntry)); |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| entry->window = window; |
| entry->root = root; |
| entry->toplevel = toplevel; |
| entry->outer_canvas = outer_canvas; |
| entry->component = component; |
| entry->next = drop_site_list; |
| drop_site_list = entry; |
| |
| return True; |
| } |
| |
| /* |
| * Returns True if the list entry for the specified window has been successfully |
| * removed from the list. Otherwise, returns False. |
| */ |
| static Boolean |
| remove_from_drop_site_list(Window window) { |
| DropSiteListEntry* entry = drop_site_list; |
| DropSiteListEntry* prev = NULL; |
| |
| while (entry != NULL) { |
| if (entry->window == window) { |
| if (prev != NULL) { |
| prev->next = entry->next; |
| } else { |
| drop_site_list = entry->next; |
| } |
| free(entry); |
| return True; |
| } |
| prev = entry; |
| entry = entry->next; |
| } |
| |
| return False; |
| } |
| |
| static jobject |
| get_component_for_window(Window window) { |
| DropSiteListEntry* entry = drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->window == window) { |
| return entry->component; |
| } |
| entry = entry->next; |
| } |
| |
| return NULL; |
| } |
| |
| static Window |
| get_root_for_window(Window window) { |
| DropSiteListEntry* entry = drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->window == window) { |
| return entry->root; |
| } |
| entry = entry->next; |
| } |
| |
| return None; |
| } |
| |
| static Window |
| get_toplevel_for_window(Window window) { |
| DropSiteListEntry* entry = drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->window == window) { |
| return entry->toplevel; |
| } |
| entry = entry->next; |
| } |
| |
| return None; |
| } |
| |
| static Window |
| get_outer_canvas_for_window(Window window) { |
| DropSiteListEntry* entry = drop_site_list; |
| |
| while (entry != NULL) { |
| if (entry->window == window) { |
| return entry->outer_canvas; |
| } |
| entry = entry->next; |
| } |
| |
| return None; |
| } |
| /******************************************************************************/ |
| |
| /******************* Delayed drop site registration stuff *********************/ |
| struct DelayedRegistrationEntryRec; |
| |
| typedef struct DelayedRegistrationEntryRec DelayedRegistrationEntry; |
| |
| struct DelayedRegistrationEntryRec { |
| Widget outer_canvas; |
| jobject component; |
| XtIntervalId timer; |
| DelayedRegistrationEntry* next; |
| }; |
| |
| static DelayedRegistrationEntry* delayed_registration_list = NULL; |
| |
| static const int DELAYED_REGISTRATION_PERIOD = 500; |
| |
| /* Timer callback. */ |
| static void |
| register_drop_site_later(XtPointer client_data, XtIntervalId* id); |
| |
| /* |
| * Enqueues the specified widget and component for delayed drop site |
| * registration. If this widget has already been registered, does nothing and |
| * returns False. Otherwise, schedules a timer callback that will repeatedly |
| * attempt to register the drop site until the registration succeeds. |
| * To remove this widget from the queue of delayed registration call |
| * remove_delayed_registration_entry(). |
| * |
| * The caller must own AWT_LOCK. |
| */ |
| static Boolean |
| add_delayed_registration_entry(Widget outer_canvas, XtPointer componentRef) { |
| DelayedRegistrationEntry* entry = delayed_registration_list; |
| |
| if (outer_canvas == NULL || componentRef == NULL) { |
| return False; |
| } |
| |
| while (entry != NULL && entry->outer_canvas != outer_canvas) { |
| entry = entry->next; |
| } |
| |
| if (entry != NULL) { |
| return False; |
| } |
| |
| entry = malloc(sizeof(DelayedRegistrationEntry)); |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| entry->outer_canvas = outer_canvas; |
| entry->component = componentRef; |
| entry->timer = XtAppAddTimeOut(awt_appContext, DELAYED_REGISTRATION_PERIOD, |
| register_drop_site_later, entry); |
| entry->next = delayed_registration_list; |
| delayed_registration_list = entry; |
| |
| return True; |
| } |
| |
| /* |
| * Unregisters the timer callback and removes this widget from the queue of |
| * delayed drop site registration. |
| * |
| * The caller must own AWT_LOCK. |
| */ |
| static Boolean |
| remove_delayed_registration_entry(Widget outer_canvas) { |
| DelayedRegistrationEntry* entry = delayed_registration_list; |
| DelayedRegistrationEntry* prev = NULL; |
| |
| if (outer_canvas == NULL) { |
| return False; |
| } |
| |
| while (entry != NULL && entry->outer_canvas != outer_canvas) { |
| prev = entry; |
| entry = entry->next; |
| } |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| if (prev != NULL) { |
| prev->next = entry->next; |
| } else { |
| delayed_registration_list = entry->next; |
| } |
| |
| if (entry->timer) { |
| XtRemoveTimeOut(entry->timer); |
| entry->timer = (XtIntervalId)0; |
| } |
| |
| free(entry); |
| |
| return True; |
| } |
| |
| static void |
| register_drop_site_later(XtPointer client_data, XtIntervalId* id) { |
| DelayedRegistrationEntry* entry = (DelayedRegistrationEntry*)client_data; |
| |
| if (XtIsRealized(entry->outer_canvas) && |
| register_drop_site(entry->outer_canvas, entry->component)) { |
| remove_delayed_registration_entry(entry->outer_canvas); |
| } else { |
| entry->timer = XtAppAddTimeOut(awt_appContext, DELAYED_REGISTRATION_PERIOD, |
| register_drop_site_later, entry); |
| } |
| } |
| /******************************************************************************/ |
| |
| static void |
| awt_dnd_cleanup() { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| |
| if (!JNU_IsNull(env, target_component)) { |
| /* Trigger dragExit */ |
| /* |
| * Note: we pass NULL native context. This indicates that response |
| * shouldn't be sent to the source. |
| */ |
| dt_postDropTargetEvent(env, target_component, 0, 0, |
| java_awt_dnd_DnDConstants_ACTION_NONE, |
| java_awt_event_MouseEvent_MOUSE_EXITED, |
| NULL); |
| } |
| |
| if (motif_top_level_leave_postponed) { |
| XClientMessageEvent* leave = &motif_top_level_leave_postponed_event; |
| if (leave->type == ClientMessage) { |
| Window win = leave->window; |
| if (is_embedding_toplevel(win)) { |
| forward_client_message_to_toplevel(win, leave); |
| } |
| } |
| } |
| |
| if (source_window != None) { |
| XSelectInput(awt_display, source_window, source_window_mask); |
| } |
| |
| source_protocol = NO_PROTOCOL; |
| source_protocol_version = 0; |
| source_window = None; |
| source_atom = None; |
| source_window_mask = 0; |
| source_actions = java_awt_dnd_DnDConstants_ACTION_NONE; |
| track_source_actions = False; |
| (*env)->DeleteGlobalRef(env, source_data_types); |
| source_data_types = NULL; |
| if (source_data_types_native != NULL) { |
| free(source_data_types_native); |
| source_data_types_native = NULL; |
| } |
| source_data_types_count = 0; |
| source_x = 0; |
| source_y = 0; |
| target_component = NULL; |
| motif_top_level_leave_postponed = False; |
| memset(&motif_top_level_leave_postponed_event, 0, |
| sizeof(XClientMessageEvent)); |
| } |
| |
| static jlongArray |
| get_data_types_array(JNIEnv* env, Atom* types, unsigned int types_count) { |
| jlongArray array = NULL; |
| jboolean isCopy; |
| jlong* jTargets; |
| #ifndef _LP64 /* Atom and jlong are different sizes in the 32-bit build */ |
| unsigned int i; |
| #endif |
| |
| if ((*env)->PushLocalFrame(env, 1) < 0) { |
| return NULL; |
| } |
| |
| array = (*env)->NewLongArray(env, types_count); |
| |
| if (JNU_IsNull(env, array)) { |
| return NULL; |
| } |
| |
| if (types_count == 0) { |
| return array; |
| } |
| |
| jTargets = (*env)->GetLongArrayElements(env, array, &isCopy); |
| if (jTargets == NULL) { |
| (*env)->PopLocalFrame(env, NULL); |
| return NULL; |
| } |
| |
| #ifdef _LP64 |
| memcpy(jTargets, types, types_count * sizeof(Atom)); |
| #else |
| for (i = 0; i < types_count; i++) { |
| jTargets[i] = (types[i] & 0xFFFFFFFFLU); |
| } |
| #endif |
| |
| (*env)->ReleaseLongArrayElements(env, array, jTargets, 0); |
| |
| array = (*env)->NewGlobalRef(env, array); |
| |
| (*env)->PopLocalFrame(env, NULL); |
| |
| return array; |
| } |
| |
| static Boolean |
| is_xdnd_drag_message_type(unsigned long message_type) { |
| return message_type == XA_XdndEnter || |
| message_type == XA_XdndPosition || |
| message_type == XA_XdndLeave || |
| message_type == XA_XdndDrop ? True : False; |
| } |
| |
| /* |
| * Returns EventConsume if the event should be consumed, |
| * EventPassAlong otherwise. |
| */ |
| static EventStatus |
| handle_xdnd_enter(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| Display* dpy = event->display; |
| long* event_data = event->data.l; |
| Window source_win = None; |
| long source_win_mask = 0; |
| unsigned int protocol_version = 0; |
| unsigned int data_types_count = 0; |
| Atom* data_types = NULL; |
| jlongArray java_data_types = NULL; |
| jint actions = java_awt_dnd_DnDConstants_ACTION_NONE; |
| Boolean track = False; |
| |
| DTRACE_PRINTLN5("%s:%d XdndEnter comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (!JNU_IsNull(env, target_component) || source_window != None || |
| source_protocol != NO_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| /* |
| * NOTE: the component can be NULL if the event was sent to the embedding |
| * toplevel. |
| */ |
| if (JNU_IsNull(env, get_component_for_window(event->window)) && |
| !is_embedding_toplevel(event->window)) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - window is not a registered drop site.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| protocol_version = |
| (event_data[1] & XDND_PROTOCOL_MASK) >> XDND_PROTOCOL_SHIFT; |
| |
| /* XDnD compliance only requires supporting version 3 and up. */ |
| if (protocol_version < XDND_MIN_PROTOCOL_VERSION) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid protocol version.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| /* Ignore the source if the protocol version is higher than we support. */ |
| if (protocol_version > XDND_PROTOCOL_VERSION) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid protocol version.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| source_win = event_data[0]; |
| |
| /* Extract the list of supported actions. */ |
| if (protocol_version < 2) { |
| /* Prior to XDnD version 2 only COPY action was supported. */ |
| actions = java_awt_dnd_DnDConstants_ACTION_COPY; |
| } else { |
| unsigned char ret; |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char *data; |
| |
| data = NULL; |
| ret = checked_XGetWindowProperty(dpy, source_win, XA_XdndActionList, |
| 0, 0xFFFF, False, XA_ATOM, &type, |
| &format, &nitems, &after, &data); |
| |
| /* Ignore the source if the window is destroyed. */ |
| if (ret == BadWindow) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| if (ret == Success) { |
| if (type == XA_ATOM && format == 32) { |
| unsigned int i; |
| Atom* action_atoms = (Atom*)data; |
| |
| for (i = 0; i < nitems; i++) { |
| actions |= xdnd_to_java_action(action_atoms[i]); |
| } |
| } |
| |
| /* |
| * According to XDnD protocol, XdndActionList is optional. |
| * If XdndActionList is not set we try to guess which actions are |
| * supported. |
| */ |
| if (type == None) { |
| actions = java_awt_dnd_DnDConstants_ACTION_COPY; |
| track = True; |
| } |
| |
| XFree(data); |
| } |
| } |
| |
| /* Extract the available data types. */ |
| if (event_data[1] & XDND_DATA_TYPES_BIT) { |
| unsigned char ret; |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char *data; |
| |
| data = NULL; |
| ret = checked_XGetWindowProperty(dpy, source_win, XA_XdndTypeList, |
| 0, 0xFFFF, False, XA_ATOM, &type, |
| &format, &nitems, &after, &data); |
| |
| /* Ignore the source if the window is destroyed. */ |
| if (ret == BadWindow) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| if (ret == Success) { |
| if (type == XA_ATOM && format == 32 && nitems > 0) { |
| data_types_count = nitems; |
| data_types = (Atom*)malloc(data_types_count * sizeof(Atom)); |
| |
| if (data_types == NULL) { |
| XFree(data); |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - malloc fails.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| memcpy((void *)data_types, (void *)data, |
| data_types_count * sizeof(Atom)); |
| } |
| |
| XFree(data); |
| } |
| } else { |
| int i; |
| data_types = (Atom*)malloc(3 * sizeof (Atom)); |
| if (data_types == NULL) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - malloc fails.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| for (i = 0; i < 3; i++) { |
| Atom j; |
| if ((j = event_data[2 + i]) != None) { |
| data_types[data_types_count++] = j; |
| } |
| } |
| } |
| |
| java_data_types = get_data_types_array(env, data_types, data_types_count); |
| |
| if (JNU_IsNull(env, java_data_types)) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - cannot create types array.", |
| __FILE__, __LINE__); |
| free((char*)data_types); |
| return EventFailure; |
| } |
| |
| /* |
| * Select for StructureNotifyMask to receive DestroyNotify in case of source |
| * crash. |
| */ |
| { |
| unsigned char ret; |
| XWindowAttributes xwa; |
| |
| XGetWindowAttributes(dpy, source_win, &xwa); |
| |
| source_win_mask = xwa.your_event_mask; |
| |
| ret = checked_XSelectInput(dpy, source_win, |
| (source_win_mask | StructureNotifyMask)); |
| |
| if (ret == BadWindow) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.", |
| __FILE__, __LINE__); |
| free((char*)data_types); |
| (*env)->DeleteGlobalRef(env, java_data_types); |
| return EventFailure; |
| } |
| } |
| |
| /* Update the global state. */ |
| source_protocol = XDND_PROTOCOL; |
| source_protocol_version = protocol_version; |
| source_window = source_win; |
| source_window_mask = source_win_mask; |
| source_actions = actions; |
| track_source_actions = track; |
| source_data_types = java_data_types; |
| source_data_types_native = data_types; |
| source_data_types_count = data_types_count; |
| |
| DTRACE_PRINTLN5("%s:%d XdndEnter handled src_win=%ld protocol=%d fmt=%d.", |
| __FILE__, __LINE__, |
| source_window, source_protocol, data_types_count); |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventConsume if the event should be consumed, |
| * EventPassAlong otherwise. |
| */ |
| static EventStatus |
| handle_xdnd_position(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| long* event_data = event->data.l; |
| Window source_win = None; |
| Time time_stamp = CurrentTime; |
| Atom action_atom = None; |
| jint action = java_awt_dnd_DnDConstants_ACTION_NONE; |
| int x = 0; |
| int y = 0; |
| jint java_event_id = 0; |
| jobject component = NULL; |
| Window receiver = None; |
| |
| DTRACE_PRINTLN5("%s:%d XdndPosition comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (source_protocol != XDND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d XdndPosition rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| source_win = event_data[0]; |
| |
| /* Ignore XDnD messages from all other windows. */ |
| if (source_window != source_win) { |
| DTRACE_PRINTLN4("%s:%d XdndPosition rejected - invalid source window cur=%ld this=%ld.", |
| __FILE__, __LINE__, source_window, source_win); |
| return EventFailure; |
| } |
| |
| x = event_data[2] >> 16; |
| y = event_data[2] & 0xFFFF; |
| |
| component = get_component_for_window(event->window); |
| |
| if (JNU_IsNull(env, component)) { |
| /* |
| * The window must be the embedding toplevel, since otherwise we would reject the |
| * XdndEnter and never get to this point. |
| */ |
| DASSERT(is_embedding_toplevel(event->window)); |
| |
| receiver = get_embedded_window(event->display, event->window, x, y); |
| |
| if (receiver != None) { |
| component = get_component_for_window(receiver); |
| } |
| } else { |
| receiver = event->window; |
| } |
| |
| /* Translate mouse position from root coordinates |
| to the target window coordinates. */ |
| if (receiver != None) { |
| Window child = None; |
| XTranslateCoordinates(event->display, |
| get_root_for_window(receiver), |
| get_outer_canvas_for_window(receiver), |
| x, y, &x, &y, &child); |
| } |
| |
| /* Time stamp - new in XDnD version 1. */ |
| if (source_protocol_version > 0) { |
| time_stamp = event_data[3]; |
| } |
| |
| /* User action - new in XDnD version 1. */ |
| if (source_protocol_version > 1) { |
| action_atom = event_data[4]; |
| } else { |
| /* The default action is XdndActionCopy */ |
| action_atom = XA_XdndActionCopy; |
| } |
| |
| action = xdnd_to_java_action(action_atom); |
| |
| if (track_source_actions) { |
| source_actions |= action; |
| } |
| |
| if (JNU_IsNull(env, component)) { |
| if (!JNU_IsNull(env, target_component)) { |
| dt_postDropTargetEvent(env, target_component, x, y, |
| java_awt_dnd_DnDConstants_ACTION_NONE, |
| java_awt_event_MouseEvent_MOUSE_EXITED, |
| NULL); |
| } |
| } else { |
| if (JNU_IsNull(env, target_component)) { |
| java_event_id = java_awt_event_MouseEvent_MOUSE_ENTERED; |
| } else { |
| java_event_id = java_awt_event_MouseEvent_MOUSE_DRAGGED; |
| } |
| |
| dt_postDropTargetEvent(env, component, x, y, action, |
| java_event_id, event); |
| } |
| |
| user_action = action; |
| source_x = x; |
| source_y = y; |
| target_component = component; |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventConsume if the event should be consumed, |
| * EventPassAlong otherwise. |
| */ |
| static EventStatus |
| handle_xdnd_leave(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| long* event_data = event->data.l; |
| Window source_win = None; |
| |
| if (source_protocol != XDND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d XdndLeave rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| source_win = event_data[0]; |
| |
| /* Ignore XDnD messages from all other windows. */ |
| if (source_window != source_win) { |
| DTRACE_PRINTLN4("%s:%d XdndLeave rejected - invalid source window cur=%ld this=%ld.", |
| __FILE__, __LINE__, source_window, source_win); |
| return EventFailure; |
| } |
| |
| awt_dnd_cleanup(); |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventConsume if the event should be consumed, |
| * EventPassAlong otherwise. |
| */ |
| static EventStatus |
| handle_xdnd_drop(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| long* event_data = event->data.l; |
| Window source_win = None; |
| |
| DTRACE_PRINTLN5("%s:%d XdndDrop comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (source_protocol != XDND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d XdndDrop rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| source_win = event_data[0]; |
| |
| /* Ignore XDnD messages from all other windows. */ |
| if (source_window != source_win) { |
| DTRACE_PRINTLN4("%s:%d XdndDrop rejected - invalid source window cur=%ld this=%ld.", |
| __FILE__, __LINE__, source_window, source_win); |
| return EventFailure; |
| } |
| |
| if (!JNU_IsNull(env, target_component)) { |
| dt_postDropTargetEvent(env, target_component, source_x, source_y, user_action, |
| java_awt_event_MouseEvent_MOUSE_RELEASED, event); |
| } |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventPassAlong if the event should be passed to the original proxy. |
| * TOP_LEVEL_ENTER should be passed to the original proxy only if the event is |
| * invalid. |
| */ |
| static EventStatus |
| handle_motif_top_level_enter(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| Display* dpy = event->display; |
| char* event_data = event->data.b; |
| unsigned char event_byte_order = 0; |
| Window source_win = None; |
| long source_win_mask = 0; |
| unsigned int protocol_version = MOTIF_DND_PROTOCOL_VERSION; |
| Atom property_atom = None; |
| unsigned int data_types_count = 0; |
| Atom* data_types = NULL; |
| jlongArray java_data_types = NULL; |
| |
| DTRACE_PRINTLN5("%s:%d TOP_LEVEL_ENTER comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (!JNU_IsNull(env, target_component) || source_window != None || |
| source_protocol != NO_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| if (JNU_IsNull(env, get_component_for_window(event->window)) && |
| !is_embedding_toplevel(event->window)) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - window is not a registered drop site.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| event_byte_order = read_card8(event_data, 1); |
| source_win = read_card32(event_data, 8, event_byte_order); |
| property_atom = read_card32(event_data, 12, event_byte_order); |
| |
| /* Extract the available data types. */ |
| { |
| unsigned char ret; |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char *data; |
| |
| data = NULL; |
| ret = checked_XGetWindowProperty(dpy, source_win, property_atom, 0, |
| 0xFFFF, False, |
| _XA_MOTIF_DRAG_INITIATOR_INFO, &type, |
| &format, &nitems, &after, &data); |
| |
| /* Ignore the source if the window is destroyed. */ |
| if (ret == BadWindow) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - invalid window.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| if (ret == BadAtom) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - invalid property atom.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| if (ret == Success) { |
| if (type == _XA_MOTIF_DRAG_INITIATOR_INFO && format == 8 && |
| nitems == MOTIF_INITIATOR_INFO_SIZE) { |
| unsigned char property_byte_order = read_card8((char*)data, 0); |
| int index = read_card16((char*)data, 2, property_byte_order); |
| |
| protocol_version = read_card8((char*)data, 1); |
| |
| if (protocol_version > MOTIF_DND_PROTOCOL_VERSION) { |
| DTRACE_PRINTLN3("%s:%d TOP_LEVEL_ENTER rejected - invalid protocol version: %d.", |
| __FILE__, __LINE__, protocol_version); |
| XFree(data); |
| return EventFailure; |
| } |
| |
| get_target_list_for_index(dpy, index, &data_types, &data_types_count); |
| } |
| |
| XFree(data); |
| } |
| } |
| |
| java_data_types = get_data_types_array(env, data_types, data_types_count); |
| |
| if (JNU_IsNull(env, java_data_types)) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - cannot create types array.", |
| __FILE__, __LINE__); |
| free((char*)data_types); |
| return EventFailure; |
| } |
| |
| /* |
| * Select for StructureNotifyMask to receive DestroyNotify in case of source |
| * crash. |
| */ |
| { |
| unsigned char ret; |
| XWindowAttributes xwa; |
| |
| XGetWindowAttributes(dpy, source_win, &xwa); |
| |
| source_win_mask = xwa.your_event_mask; |
| |
| ret = checked_XSelectInput(dpy, source_win, |
| (source_win_mask | StructureNotifyMask)); |
| |
| if (ret == BadWindow) { |
| DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.", |
| __FILE__, __LINE__); |
| free((char*)data_types); |
| (*env)->DeleteGlobalRef(env, java_data_types); |
| return EventFailure; |
| } |
| } |
| |
| source_protocol = MOTIF_DND_PROTOCOL; |
| source_protocol_version = protocol_version; |
| source_window = source_win; |
| source_atom = property_atom; |
| source_window_mask = source_win_mask; |
| /* |
| * TOP_LEVEL_ENTER doesn't communicate the list of supported actions |
| * They are provided in DRAG_MOTION. |
| */ |
| source_actions = java_awt_dnd_DnDConstants_ACTION_NONE; |
| track_source_actions = False; |
| source_data_types = java_data_types; |
| source_data_types_native = data_types; |
| source_data_types_count = data_types_count; |
| DTRACE_PRINTLN6("%s:%d TOP_LEVEL_ENTER comp=%d src_win=%ld protocol=%d fmt=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol, data_types_count); |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventPassAlong if the event should be passed to the original proxy. |
| * DRAG_MOTION event shouldn't be passed to the original proxy only if it is |
| * a valid event and the mouse coordinates passed in it specify the point over |
| * a Java component in this JVM. |
| */ |
| static EventStatus |
| handle_motif_drag_motion(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| char* event_data = event->data.b; |
| unsigned char event_reason = 0; |
| unsigned char event_byte_order = 0; |
| Window source_win = None; |
| CARD16 flags = 0; |
| unsigned char motif_action = 0; |
| unsigned char motif_actions = 0; |
| jint java_action = java_awt_dnd_DnDConstants_ACTION_NONE; |
| jint java_actions = java_awt_dnd_DnDConstants_ACTION_NONE; |
| int x = 0; |
| int y = 0; |
| jint java_event_id = 0; |
| jobject component = NULL; |
| |
| DTRACE_PRINTLN5("%s:%d DRAG_MOTION comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (source_protocol != MOTIF_DND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d DRAG_MOTION rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| event_reason = read_card8(event_data, 0) & MOTIF_MESSAGE_REASON_MASK; |
| event_byte_order = read_card8(event_data, 1); |
| |
| flags = read_card16(event_data, 2, event_byte_order); |
| |
| motif_action = (flags & MOTIF_DND_ACTION_MASK) >> MOTIF_DND_ACTION_SHIFT; |
| motif_actions = (flags & MOTIF_DND_ACTIONS_MASK) >> MOTIF_DND_ACTIONS_SHIFT; |
| |
| java_action = motif_to_java_actions(motif_action); |
| java_actions = motif_to_java_actions(motif_actions); |
| |
| /* Append source window id to the event data, so that we can send the |
| response properly. */ |
| { |
| Window win = source_window; |
| void* p = &event->data.b[12]; |
| if (event_byte_order != MOTIF_BYTE_ORDER) { |
| SWAP4BYTES(win); |
| } |
| write_card32(&p, (CARD32)win); |
| } |
| |
| component = get_component_for_window(event->window); |
| |
| if (event_reason == OPERATION_CHANGED) { |
| /* OPERATION_CHANGED event doesn't provide coordinates, so we use |
| previously stored position and component ref. */ |
| x = source_x; |
| y = source_y; |
| |
| if (JNU_IsNull(env, component)) { |
| component = target_component; |
| } |
| } else { |
| Window receiver = None; |
| |
| x = read_card16(event_data, 8, event_byte_order); |
| y = read_card16(event_data, 10, event_byte_order); |
| |
| if (JNU_IsNull(env, component)) { |
| /* |
| * The window must be the embedding toplevel, since otherwise we |
| * would reject the TOP_LEVEL_ENTER and never get to this point. |
| */ |
| DASSERT(is_embedding_toplevel(event->window)); |
| |
| receiver = get_embedded_window(event->display, event->window, x, y); |
| |
| if (receiver != None) { |
| component = get_component_for_window(receiver); |
| } |
| } else { |
| receiver = event->window; |
| } |
| |
| /* Translate mouse position from root coordinates |
| to the target window coordinates. */ |
| if (receiver != None) { |
| Window child = None; |
| XTranslateCoordinates(event->display, |
| get_root_for_window(receiver), |
| get_outer_canvas_for_window(receiver), |
| x, y, &x, &y, &child); |
| } |
| } |
| |
| if (JNU_IsNull(env, component)) { |
| if (!JNU_IsNull(env, target_component)) { |
| /* Triggers dragExit */ |
| dt_postDropTargetEvent(env, target_component, x, y, |
| java_awt_dnd_DnDConstants_ACTION_NONE, |
| java_awt_event_MouseEvent_MOUSE_EXITED, |
| NULL); |
| } |
| } else { |
| if (JNU_IsNull(env, target_component)) { |
| /* Triggers dragEnter */ |
| java_event_id = java_awt_event_MouseEvent_MOUSE_ENTERED; |
| } else { |
| /* Triggers dragOver */ |
| java_event_id = java_awt_event_MouseEvent_MOUSE_DRAGGED; |
| } |
| |
| dt_postDropTargetEvent(env, component, x, y, java_action, java_event_id, |
| event); |
| } |
| |
| source_actions = java_actions; |
| track_source_actions = False; |
| user_action = java_action; |
| source_x = x; |
| source_y = y; |
| target_component = component; |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventPassAlong if the event should be passed to the original proxy. |
| * TOP_LEVEL_LEAVE should be passed to the original proxy only if the event |
| * is invalid. |
| */ |
| static EventStatus |
| handle_motif_top_level_leave(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| char* event_data = event->data.b; |
| unsigned char event_byte_order = 0; |
| Window source_win = None; |
| |
| DTRACE_PRINTLN5("%s:%d TOP_LEVEL_LEAVE comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (source_protocol != MOTIF_DND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_LEAVE rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| event_byte_order = read_card8(event_data, 1); |
| source_win = read_card32(event_data, 8, event_byte_order); |
| |
| /* Ignore Motif DnD messages from all other windows. */ |
| if (source_window != source_win) { |
| DTRACE_PRINTLN4("%s:%d TOP_LEVEL_LEAVE rejected - invalid source window cur=%ld this=%ld.", |
| __FILE__, __LINE__, source_window, source_win); |
| return EventFailure; |
| } |
| |
| /* |
| * Postpone upcall to java, so that we can abort it in case |
| * if drop immediatelly follows (see BugTraq ID 4395290). |
| * Send a dummy ClientMessage event to guarantee that a postponed java |
| * upcall will be processed. |
| */ |
| motif_top_level_leave_postponed = True; |
| { |
| XClientMessageEvent dummy; |
| Window proxy; |
| |
| dummy.display = event->display; |
| dummy.type = ClientMessage; |
| dummy.window = event->window; |
| dummy.format = 32; |
| dummy.message_type = None; |
| |
| /* |
| * If this is an embedded drop site, the event should go to the |
| * awt_root_window as this is a proxy for all embedded drop sites. |
| * Otherwise the event should go to the event->window, as we don't use |
| * proxies for normal drop sites. |
| */ |
| if (is_embedding_toplevel(event->window)) { |
| proxy = get_awt_root_window(); |
| } else { |
| proxy = event->window; |
| } |
| |
| XSendEvent(event->display, proxy, False, NoEventMask, |
| (XEvent*)&dummy); |
| } |
| |
| return EventSuccess; |
| } |
| |
| /* |
| * Returns EventPassAlong if the event should be passed to the original proxy. |
| * DROP_START event shouldn't be passed to the original proxy only if it is |
| * a valid event and the mouse coordinates passed in it specify the point over |
| * a Java component in this JVM. |
| */ |
| static EventStatus |
| handle_motif_drop_start(XClientMessageEvent* event) { |
| JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| char* event_data = event->data.b; |
| unsigned char event_byte_order = 0; |
| Window source_win = None; |
| Atom property_atom = None; |
| CARD16 flags = 0; |
| unsigned char motif_action = 0; |
| unsigned char motif_actions = 0; |
| jint java_action = java_awt_dnd_DnDConstants_ACTION_NONE; |
| jint java_actions = java_awt_dnd_DnDConstants_ACTION_NONE; |
| int x = 0; |
| int y = 0; |
| jobject component = NULL; |
| Window receiver = None; |
| |
| DTRACE_PRINTLN5("%s:%d DROP_START comp=%X src_win=%ld protocol=%d.", |
| __FILE__, __LINE__, |
| target_component, source_window, source_protocol); |
| |
| if (source_protocol != MOTIF_DND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d DROP_START rejected - invalid state.", |
| __FILE__, __LINE__); |
| return EventFailure; |
| } |
| |
| event_byte_order = read_card8(event_data, 1); |
| source_win = read_card32(event_data, 16, event_byte_order); |
| |
| /* Ignore Motif DnD messages from all other windows. */ |
| if (source_window != source_win) { |
| DTRACE_PRINTLN4("%s:%d DROP_START rejected - invalid source window cur=%ld this=%ld.", |
| __FILE__, __LINE__, source_window, source_win); |
| return EventFailure; |
| } |
| |
| property_atom = read_card32(event_data, 12, event_byte_order); |
| |
| flags = read_card16(event_data, 2, event_byte_order); |
| |
| motif_action = (flags & MOTIF_DND_ACTION_MASK) >> MOTIF_DND_ACTION_SHIFT; |
| motif_actions = (flags & MOTIF_DND_ACTIONS_MASK) >> MOTIF_DND_ACTIONS_SHIFT; |
| |
| java_action = motif_to_java_actions(motif_action); |
| java_actions = motif_to_java_actions(motif_actions); |
| |
| x = read_card16(event_data, 8, event_byte_order); |
| y = read_card16(event_data, 10, event_byte_order); |
| |
| source_actions = java_actions; |
| |
| component = get_component_for_window(event->window); |
| |
| if (JNU_IsNull(env, component)) { |
| /* |
| * The window must be the embedding toplevel, since otherwise we would reject the |
| * TOP_LEVEL_ENTER and never get to this point. |
| */ |
| DASSERT(is_embedding_toplevel(event->window)); |
| |
| receiver = get_embedded_window(event->display, event->window, x, y); |
| |
| if (receiver != None) { |
| component = get_component_for_window(receiver); |
| } |
| } else { |
| receiver = event->window; |
| } |
| |
| /* Translate mouse position from root coordinates |
| to the target window coordinates. */ |
| if (receiver != None) { |
| Window child = None; |
| XTranslateCoordinates(event->display, |
| get_root_for_window(receiver), |
| get_outer_canvas_for_window(receiver), |
| x, y, &x, &y, &child); |
| } |
| |
| if (JNU_IsNull(env, component)) { |
| if (!JNU_IsNull(env, target_component)) { |
| /* Triggers dragExit */ |
| dt_postDropTargetEvent(env, target_component, x, y, |
| java_awt_dnd_DnDConstants_ACTION_NONE, |
| java_awt_event_MouseEvent_MOUSE_EXITED, |
| NULL); |
| } |
| } else { |
| dt_postDropTargetEvent(env, component, x, y, java_action, |
| java_awt_event_MouseEvent_MOUSE_RELEASED, |
| event); |
| } |
| |
| return EventSuccess; |
| } |
| |
| static void |
| send_enter_message_to_toplevel(Window toplevel, XClientMessageEvent* xclient) { |
| XClientMessageEvent enter; |
| |
| if (source_protocol == XDND_PROTOCOL) { |
| enter.display = xclient->display; |
| enter.type = ClientMessage; |
| enter.window = toplevel; |
| enter.format = 32; |
| enter.message_type = XA_XdndEnter; |
| enter.data.l[0] = xclient->data.l[0]; /* XID of the source window */ |
| enter.data.l[1] = source_protocol_version << XDND_PROTOCOL_SHIFT; |
| enter.data.l[1] |= source_data_types_count > 3 ? XDND_DATA_TYPES_BIT : 0; |
| enter.data.l[2] = |
| source_data_types_count > 0 ? source_data_types_native[0] : None; |
| enter.data.l[3] = |
| source_data_types_count > 1 ? source_data_types_native[1] : None; |
| enter.data.l[4] = |
| source_data_types_count > 2 ? source_data_types_native[2] : None; |
| } else if (source_protocol == MOTIF_DND_PROTOCOL) { |
| int reason = (int)(xclient->data.b[0] & MOTIF_MESSAGE_REASON_MASK); |
| unsigned char byte_order = xclient->data.b[1]; |
| |
| enter.display = xclient->display; |
| enter.type = ClientMessage; |
| enter.window = toplevel; |
| 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(user_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, byte_order); |
| write_card16(&p, flags); |
| { |
| Time time_stamp = read_card32(xclient->data.b, 4, byte_order); |
| Window src_window = source_window; |
| Atom motif_atom = _XA_MOTIF_ATOM_0; |
| |
| if (byte_order != MOTIF_BYTE_ORDER) { |
| SWAP4BYTES(time_stamp); |
| SWAP4BYTES(src_window); |
| SWAP4BYTES(motif_atom); |
| } |
| write_card32(&p, time_stamp); |
| write_card32(&p, src_window); |
| write_card32(&p, motif_atom); |
| } |
| } |
| } else { |
| return; |
| } |
| |
| forward_client_message_to_toplevel(toplevel, &enter); |
| } |
| |
| static void |
| send_leave_message_to_toplevel(Window toplevel, XClientMessageEvent* xclient) { |
| XClientMessageEvent leave; |
| |
| if (source_protocol == XDND_PROTOCOL) { |
| leave.display = xclient->display; |
| leave.type = ClientMessage; |
| leave.window = toplevel; |
| leave.format = 32; |
| leave.message_type = XA_XdndLeave; |
| leave.data.l[0] = xclient->data.l[0]; /* XID of the source window */ |
| leave.data.l[1] = 0; /* flags */ |
| } else if (source_protocol == MOTIF_DND_PROTOCOL) { |
| int reason = (int)(xclient->data.b[0] & MOTIF_MESSAGE_REASON_MASK); |
| unsigned char byte_order = xclient->data.b[1]; |
| |
| leave.display = xclient->display; |
| leave.type = ClientMessage; |
| leave.window = toplevel; |
| leave.format = 8; |
| leave.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE; |
| |
| { |
| void* p = &leave.data.b[0]; |
| int flags = 0; |
| |
| write_card8(&p, TOP_LEVEL_LEAVE | MOTIF_MESSAGE_FROM_INITIATOR); |
| write_card8(&p, byte_order); |
| |
| { |
| Time time_stamp = read_card32(xclient->data.b, 4, byte_order); |
| Window src_window = source_window; |
| |
| if (byte_order != MOTIF_BYTE_ORDER) { |
| SWAP4BYTES(time_stamp); |
| SWAP4BYTES(src_window); |
| } |
| write_card32(&p, time_stamp); |
| write_card32(&p, src_window); |
| } |
| } |
| } else { |
| return; |
| } |
| |
| forward_client_message_to_toplevel(toplevel, &leave); |
| } |
| |
| static void |
| post_process_client_message(XClientMessageEvent* xclient, EventStatus status, |
| EventType type) { |
| Window win = xclient->window; |
| Boolean postponed_leave = motif_top_level_leave_postponed; |
| |
| motif_top_level_leave_postponed = False; |
| |
| if (is_embedding_toplevel(win)) { |
| Boolean server_grabbed = False; |
| |
| if (postponed_leave) { |
| XClientMessageEvent* leave = &motif_top_level_leave_postponed_event; |
| DASSERT(leave->type == ClientMessage && type == DropEvent); |
| /* Grab the server to ensure that no event is sent between |
| the TOP_LEVEL_LEAVE and the next message. */ |
| XGrabServer(awt_display); |
| forward_client_message_to_toplevel(leave->window, leave); |
| memset(&motif_top_level_leave_postponed_event, 0, |
| sizeof(XClientMessageEvent)); |
| } |
| |
| /* |
| * This code forwards drag notifications to the browser according to the |
| * following rules: |
| * - the messages that we failed to process are always forwarded to the |
| * browser; |
| * - MotionEvents and DropEvents are forwarded if and only if the drag |
| * is not over a plugin window; |
| * - XDnD: EnterEvents and LeaveEvents are never forwarded, instead, we |
| * send synthesized EnterEvents or LeaveEvents when the drag |
| * respectively exits or enters plugin windows; |
| * - Motif DnD: EnterEvents and LeaveEvents are always forwarded. |
| * Synthetic EnterEvents and LeaveEvents are needed, because the XDnD drop |
| * site implemented Netscape 6.2 has a nice feature: when it receives |
| * the first XdndPosition it continuously sends XdndStatus messages to |
| * the source (every 100ms) until the drag terminates or leaves the drop |
| * site. When the mouse is dragged over plugin window embedded in the |
| * browser frame, these XdndStatus messages are mixed with the XdndStatus |
| * messages sent from the plugin. |
| * For Motif DnD, synthetic events cause Motif warnings being displayed, |
| * so these events are always forwarded. However, Motif DnD drop site in |
| * Netscape 6.2 is implemented in the same way, so there could be similar |
| * problems if the drag source choose Motif DnD for communication. |
| */ |
| switch (status) { |
| case EventFailure: |
| forward_client_message_to_toplevel(win, xclient); |
| break; |
| case EventSuccess: |
| { |
| /* True iff the previous notification was MotionEvent and it was |
| forwarded to the browser. */ |
| static Boolean motion_passed_along = False; |
| |
| Boolean motif_protocol = |
| xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE; |
| |
| switch (type) { |
| case MotionEvent: |
| if (JNU_IsNull(env, target_component)) { |
| if (!motion_passed_along && !motif_protocol) { |
| send_enter_message_to_toplevel(win, xclient); |
| } |
| forward_client_message_to_toplevel(win, xclient); |
| motion_passed_along = True; |
| } else { |
| if (motion_passed_along && !motif_protocol) { |
| send_leave_message_to_toplevel(win, xclient); |
| } |
| motion_passed_along = False; |
| } |
| break; |
| case DropEvent: |
| if (JNU_IsNull(env, target_component)) { |
| forward_client_message_to_toplevel(win, xclient); |
| /* The last chance to cleanup. */ |
| awt_dnd_cleanup(); |
| } |
| motion_passed_along = False; |
| break; |
| case EnterEvent: |
| case LeaveEvent: |
| if (motif_protocol) { |
| forward_client_message_to_toplevel(win, xclient); |
| } |
| motion_passed_along = False; |
| break; |
| } |
| } |
| } |
| |
| if (postponed_leave) { |
| XUngrabServer(awt_display); |
| } |
| } |
| } |
| |
| /* |
| * Returns True if the event is processed and shouldn't be passed along to Java. |
| * Otherwise, return False. |
| */ |
| Boolean |
| awt_dnd_dt_process_event(XEvent* event) { |
| Display* dpy = event->xany.display; |
| EventStatus status = EventFailure; |
| EventType type = UnknownEvent; |
| |
| if (event->type == DestroyNotify) { |
| if (event->xany.window == source_window) { |
| awt_dnd_cleanup(); |
| } |
| /* pass along */ |
| return False; |
| } |
| |
| if (event->type == PropertyNotify) { |
| if (is_embedding_toplevel(event->xany.window)) { |
| Atom atom = event->xproperty.atom; |
| /* |
| * If some other client replaced the XDnD or Motif DnD proxy with |
| * another window we set the proxy back to the awt_root_window |
| * and update the entry in the embedded_drop_site_list. |
| * This code is needed, as for example Netscape 4.7 resets the proxy |
| * when the browser shell is resized. |
| */ |
| if (atom == _XA_MOTIF_DRAG_RECEIVER_INFO) { |
| Window prev_motif_proxy; |
| ProxyRegistrationStatus status; |
| status = set_motif_proxy(event->xany.display, event->xany.window, |
| get_awt_root_window(), &prev_motif_proxy); |
| if (status != RegFailure && status != RegAlreadyRegistered) { |
| set_motif_proxy_for_toplevel(event->xany.window, |
| prev_motif_proxy, |
| status == RegOverride); |
| } |
| } |
| |
| if (atom == XA_XdndAware || atom == XA_XdndProxy) { |
| Window prev_xdnd_proxy; |
| unsigned int prev_protocol_version; |
| ProxyRegistrationStatus status; |
| status = set_xdnd_proxy(event->xany.display, event->xany.window, |
| get_awt_root_window(), &prev_xdnd_proxy, |
| &prev_protocol_version); |
| if (status != RegFailure && status != RegAlreadyRegistered) { |
| set_xdnd_proxy_for_toplevel(event->xany.window, |
| prev_xdnd_proxy, |
| prev_protocol_version, |
| status == RegOverride); |
| } |
| } |
| } |
| /* pass along */ |
| return False; |
| } |
| |
| if (event->type != ClientMessage) { |
| return False; |
| } |
| |
| if (get_component_for_window(event->xany.window) == NULL && |
| !is_embedding_toplevel(event->xany.window)) { |
| return False; |
| } |
| |
| if (motif_top_level_leave_postponed) { |
| /* Sanity check. */ |
| if (source_protocol != MOTIF_DND_PROTOCOL) { |
| DTRACE_PRINTLN2("%s:%d TOP_LEVEL_LEAVE rejected - invalid state.", |
| __FILE__, __LINE__); |
| awt_dnd_cleanup(); |
| } else if (event->xclient.message_type == |
| _XA_MOTIF_DRAG_AND_DROP_MESSAGE) { |
| unsigned char first_byte = event->xclient.data.b[0]; |
| unsigned char reason = first_byte & MOTIF_MESSAGE_REASON_MASK; |
| unsigned char origin = first_byte & MOTIF_MESSAGE_SENDER_MASK; |
| |
| if (origin == MOTIF_MESSAGE_FROM_INITIATOR && |
| reason != DROP_START) { |
| awt_dnd_cleanup(); |
| } |
| } else { |
| awt_dnd_cleanup(); |
| } |
| } |
| |
| if (event->xclient.message_type == XA_XdndEnter) { |
| status = handle_xdnd_enter(&event->xclient); |
| type = EnterEvent; |
| } else if (event->xclient.message_type == XA_XdndPosition) { |
| status = handle_xdnd_position(&event->xclient); |
| type = MotionEvent; |
| } else if (event->xclient.message_type == XA_XdndLeave) { |
| status = handle_xdnd_leave(&event->xclient); |
| type = LeaveEvent; |
| } else if (event->xclient.message_type == XA_XdndDrop) { |
| status = handle_xdnd_drop(&event->xclient); |
| type = DropEvent; |
| } else if (event->xclient.message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) { |
| unsigned char reason = event->xclient.data.b[0] & MOTIF_MESSAGE_REASON_MASK; |
| unsigned char origin = event->xclient.data.b[0] & MOTIF_MESSAGE_SENDER_MASK; |
| |
| /* Only initiator messages should be handled. */ |
| if (origin == MOTIF_MESSAGE_FROM_INITIATOR) { |
| switch (reason) { |
| case DRAG_MOTION: |
| case OPERATION_CHANGED: |
| status = handle_motif_drag_motion(&event->xclient); |
| type = MotionEvent; |
| break; |
| case TOP_LEVEL_ENTER: |
| status = handle_motif_top_level_enter(&event->xclient); |
| type = EnterEvent; |
| break; |
| case TOP_LEVEL_LEAVE: |
| status = handle_motif_top_level_leave(&event->xclient); |
| type = LeaveEvent; |
| break; |
| case DROP_START: |
| status = handle_motif_drop_start(&event->xclient); |
| type = DropEvent; |
| break; |
| } |
| } |
| } else { |
| /* Unknown message type. */ |
| return False; |
| } |
| |
| /* |
| * We need to handle a special case here: Motif DnD protocol prescribed that |
| * DROP_START message should always be preceeded with TOP_LEVEL_LEAVE |
| * message. We need to cleanup on TOP_LEVEL_LEAVE message, but DROP_START |
| * wouldn't be processed properly. Instead we postpone the cleanup and |
| * send a dummy client message to ourselves. If dummy arrives first we do a |
| * normal cleanup. If DROP_START arrives before the dummy we discard delayed |
| * cleanup. |
| * In case of forwarding events from an embedded Java app to an embedding |
| * Java app it could happen that the embedding app receives the dummy before |
| * the DROP_START message arrives from the embedding app. In this case the |
| * drop operation on the embedding app fails to complete. |
| * To resolve this problem we postpone forwarding of TOP_LEVEL_LEAVE message |
| * until the next client message is about to be forwarded. |
| */ |
| if (motif_top_level_leave_postponed && type == LeaveEvent) { |
| /* motif_top_level_leave_postponed can be set only if the latest client |
| message has been processed successfully. */ |
| DASSERT(status == EventSuccess); |
| memcpy(&motif_top_level_leave_postponed_event, &event->xclient, |
| sizeof(XClientMessageEvent)); |
| } else { |
| post_process_client_message(&event->xclient, status, type); |
| } |
| |
| return True; |
| } |
| |
| static Boolean |
| register_xdnd_drop_site(Display* dpy, Window toplevel, Window window) { |
| unsigned char ret; |
| Atom version_atom = XDND_PROTOCOL_VERSION; |
| |
| ret = checked_XChangeProperty(dpy, window, XA_XdndAware, XA_ATOM, 32, |
| PropModeReplace, |
| (unsigned char*)&version_atom, 1); |
| |
| return (ret == Success); |
| } |
| |
| static Boolean |
| register_motif_drop_site(Display* dpy, Window toplevel, Window window) { |
| unsigned char status; |
| size_t data_size = MOTIF_RECEIVER_INFO_SIZE; |
| char* data = malloc(data_size); |
| void* p = data; |
| |
| if (data == NULL) { |
| DTRACE_PRINTLN2("%s:%d malloc failed.", __FILE__, __LINE__); |
| return False; |
| } |
| |
| write_card8(&p, MOTIF_BYTE_ORDER); |
| write_card8(&p, MOTIF_DND_PROTOCOL_VERSION); /* protocol version */ |
| write_card8(&p, MOTIF_DYNAMIC_STYLE); /* protocol style */ |
| write_card8(&p, 0); /* pad */ |
| write_card32(&p, window); /* proxy window */ |
| write_card16(&p, 0); /* num_drop_sites */ |
| write_card16(&p, 0); /* pad */ |
| write_card32(&p, data_size); |
| |
| status = checked_XChangeProperty(dpy, window, _XA_MOTIF_DRAG_RECEIVER_INFO, |
| _XA_MOTIF_DRAG_RECEIVER_INFO, 8, PropModeReplace, |
| (unsigned char*)data, data_size); |
| |
| free(data); |
| |
| return (status == Success); |
| } |
| |
| static Window |
| find_toplevel_window(Display* dpy, Window window) { |
| Window ret = None; |
| Window root = None; |
| Window parent = None; |
| Window *children; |
| unsigned int nchildren; |
| |
| int status; |
| |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char *data; |
| |
| /* Traverse the ancestor tree from window up to the root and find |
| the top-level client window nearest to the root. */ |
| do { |
| type = None; |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, window, XA_WM_STATE, 0, 0, False, |
| AnyPropertyType, &type, &format, &nitems, |
| &after, &data); |
| |
| if (status == Success) { |
| XFree(data); |
| } |
| |
| if (type != None) { |
| ret = window; |
| } |
| |
| if (!XQueryTree(dpy, window, &root, &parent, &children, &nchildren)) { |
| return None; |
| } |
| |
| XFree(children); |
| |
| window = parent; |
| } while (window != root); |
| |
| return ret; |
| } |
| |
| static Boolean |
| register_drop_site(Widget outer_canvas, XtPointer componentRef) { |
| Display* dpy = XtDisplay(outer_canvas); |
| Widget shell = NULL; |
| /* Shell window. */ |
| Window window = None; |
| Window root = None; |
| Window toplevel = None; |
| |
| for (shell = outer_canvas; shell != NULL && !XtIsShell(shell); |
| shell = XtParent(shell)); |
| |
| if (shell == NULL || !XtIsRealized(shell)) { |
| DTRACE_PRINTLN2("%s:%d Cannot find a realized shell for the widget.", |
| __FILE__, __LINE__); |
| return False; |
| } |
| |
| window = XtWindow(shell); |
| |
| if (!awt_dnd_init(dpy)) { |
| DTRACE_PRINTLN2("%s:%d Fail to initialize.", __FILE__, __LINE__); |
| return False; |
| } |
| |
| { |
| XWindowAttributes xwa; |
| |
| if (!XGetWindowAttributes(dpy, window, &xwa)) { |
| DTRACE_PRINTLN2("%s:%d XGetWindowAttributes failed.", __FILE__, __LINE__); |
| return False; |
| } |
| |
| root = xwa.root; |
| |
| if (root == None) { |
| DTRACE_PRINTLN2("%s:%d Bad root.", __FILE__, __LINE__); |
| return False; |
| } |
| } |
| |
| toplevel = find_toplevel_window(dpy, window); |
| |
| /* |
| * No window with WM_STATE property is found. |
| * Since the window can be a plugin window reparented to the browser |
| * toplevel, we cannot determine which window will eventually have WM_STATE |
| * property set. So we schedule a timer callback that will periodically |
| * attempt to find an ancestor with WM_STATE and register the drop site |
| * appropriately. |
| */ |
| if (toplevel == None) { |
| add_delayed_registration_entry(outer_canvas, componentRef); |
| return False; |
| } |
| |
| if (toplevel == window) { |
| Boolean xdnd_registered = False; |
| Boolean motif_registered = False; |
| |
| xdnd_registered = register_xdnd_drop_site(dpy, toplevel, window); |
| |
| motif_registered = register_motif_drop_site(dpy, toplevel, window); |
| |
| if (!xdnd_registered && !motif_registered) { |
| DTRACE_PRINTLN2("%s:%d Failed to register.", __FILE__, __LINE__); |
| return False; |
| } |
| } else { |
| if (!add_to_embedded_drop_site_list(dpy, root, toplevel, window)) { |
| DTRACE_PRINTLN2("%s:%d Failed to init proxy.", __FILE__, __LINE__); |
| return False; |
| } |
| } |
| |
| /* There is no need to update the window for the component later, since the |
| window is destroyed only when the component is disposed in which case the |
| drop site will be unregistered as well. */ |
| if (add_to_drop_site_list(window, root, toplevel, XtWindow(outer_canvas), |
| (jobject)componentRef)) { |
| DTRACE_PRINTLN2("%s:%d Drop site registered.", __FILE__, __LINE__); |
| return True; |
| } else { |
| DTRACE_PRINTLN2("%s:%d Failed to register.", __FILE__, __LINE__); |
| return False; |
| } |
| } |
| |
| static void |
| register_drop_site_when_realized(Widget outer_canvas, XtPointer client_data, |
| XEvent *event, Boolean *dontSwallow) { |
| if (XtIsRealized(outer_canvas)) { |
| XtRemoveEventHandler(outer_canvas, StructureNotifyMask, False, |
| register_drop_site_when_realized, client_data); |
| |
| register_drop_site(outer_canvas, client_data); |
| } |
| } |
| |
| /* |
| * Registers the top-level Window that contains the specified widget as a drop |
| * site that supports XDnD and Motif DnD protocols. |
| * If the registration fails for some reason, adds an event handler that will |
| * attempt to register the drop site later. |
| * |
| * Returns True if the drop site is registered successfully. |
| */ |
| static Boolean |
| awt_dnd_register_drop_site(Widget outer_canvas, XtPointer componentRef) { |
| if (XtIsRealized(outer_canvas)) { |
| return register_drop_site(outer_canvas, componentRef); |
| } else { |
| XtAddEventHandler(outer_canvas, StructureNotifyMask, False, |
| register_drop_site_when_realized, |
| componentRef); |
| |
| DTRACE_PRINTLN2("%s:%d Unrealized shell. Register later.", |
| __FILE__, __LINE__); |
| |
| return True; |
| } |
| } |
| |
| /* |
| * Unregisters the drop site associated with the top-level Window that contains |
| * the specified widget . |
| * |
| * Returns True if completes successfully, False otherwise. |
| */ |
| static Boolean |
| awt_dnd_unregister_drop_site(Widget outer_canvas, XtPointer componentRef) { |
| Widget shell = NULL; |
| |
| XtRemoveEventHandler(outer_canvas, StructureNotifyMask, False, |
| register_drop_site_when_realized, componentRef); |
| |
| remove_delayed_registration_entry(outer_canvas); |
| |
| for (shell = outer_canvas; shell != NULL && !XtIsShell(shell); |
| shell = XtParent(shell)); |
| |
| if (shell != NULL && XtIsShell(shell) && XtIsRealized(shell)) { |
| Window win = XtWindow(shell); |
| Window toplevel = get_toplevel_for_window(win); |
| /* |
| * Cleanup the global state if this drop site participate in the current |
| * drag operation. Particularly, this allows to delete global ref to the |
| * component safely. |
| */ |
| if (get_component_for_window(win) == target_component) { |
| awt_dnd_cleanup(); |
| } |
| if (toplevel != win) { |
| remove_from_embedded_drop_site_list(awt_display, toplevel, win); |
| } |
| return remove_from_drop_site_list(win); |
| } |
| |
| return True; |
| } |
| |
| /**************************** XEmbed server DnD support ***********************/ |
| |
| /* |
| * |
| * |
| */ |
| Boolean |
| register_xembed_drop_site(JNIEnv* env, Display* dpy, jobject server, |
| Window serverHandle, Window clientHandle) { |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char* data; |
| unsigned char ret; |
| unsigned int protocol_version; |
| |
| Window xdnd_proxy = None; |
| unsigned int xdnd_protocol_version = 0; |
| Boolean xdnd_override = False; |
| |
| if (!awt_dnd_init(dpy)) { |
| DTRACE_PRINTLN2("%s:%d Fail to initialize.", __FILE__, __LINE__); |
| return False; |
| } |
| |
| /* Get the XDnD protocol version and XDnD proxy of the XEmbed client. */ |
| data = NULL; |
| ret = checked_XGetWindowProperty(dpy, clientHandle, XA_XdndAware, 0, 1, |
| False, AnyPropertyType, &type, &format, |
| &nitems, &after, &data); |
| |
| /* XEmbed client doesn't have an associated XDnD drop site - |
| do nothing and return True to indicate success. */ |
| if (ret != Success || data == NULL || nitems == 0 || type != XA_ATOM) { |
| XFree(data); |
| return False; |
| } |
| |
| protocol_version = *((unsigned int*)data); |
| |
| XFree(data); |
| |
| if (protocol_version < XDND_MIN_PROTOCOL_VERSION) { |
| return False; |
| } |
| |
| xdnd_protocol_version = protocol_version; |
| |
| /* XdndProxy is not supported prior to XDnD version 4 */ |
| if (protocol_version >= 4) { |
| int status; |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, clientHandle, XA_XdndProxy, 0, 1, |
| False, XA_WINDOW, &type, &format, |
| &nitems, &after, &data); |
| |
| if (status == Success && data != NULL && type == XA_WINDOW) { |
| xdnd_proxy = *((Window*)data); |
| |
| if (xdnd_proxy != None) { |
| XFree(data); |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndProxy, |
| 0, 1, False, XA_WINDOW, &type, |
| &format, &nitems, &after, |
| &data); |
| |
| if (status != Success || data == NULL || type != XA_WINDOW || |
| *((Window*)data) != xdnd_proxy) { |
| /* Ignore invalid proxy. */ |
| xdnd_proxy = None; |
| } |
| } |
| |
| if (xdnd_proxy != None) { |
| XFree(data); |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndAware, 0, 1, |
| False, AnyPropertyType, &type, |
| &format, &nitems, &after, &data); |
| |
| if (status == Success && data != NULL && type == XA_ATOM) { |
| unsigned int proxy_version = *((unsigned int*)data); |
| |
| if (proxy_version != protocol_version) { |
| /* Ignore invalid proxy. */ |
| xdnd_proxy = None; |
| } |
| } else { |
| /* Ignore invalid proxy. */ |
| xdnd_proxy = None; |
| } |
| } |
| } |
| |
| XFree(data); |
| } |
| |
| set_xembed_drop_target(env, server); |
| |
| /* Add protocol specific entries for the embedded window. */ |
| /* Only XDnD protocol is supported for XEmbed clients. */ |
| { |
| EmbeddedDropSiteProtocolListEntry* xdnd_entry = NULL; |
| |
| xdnd_entry = malloc(sizeof(EmbeddedDropSiteProtocolListEntry)); |
| |
| if (xdnd_entry == NULL) { |
| return False; |
| } |
| |
| xdnd_entry->window = clientHandle; |
| xdnd_entry->proxy = xdnd_proxy; |
| xdnd_entry->protocol_version = xdnd_protocol_version; |
| xdnd_entry->overriden = True; |
| xdnd_entry->next = embedded_xdnd_protocol_list; |
| embedded_xdnd_protocol_list = xdnd_entry; |
| } |
| |
| { |
| EmbeddedDropSiteListEntry* entry = NULL; |
| Window* sites = NULL; |
| |
| entry = malloc(sizeof(EmbeddedDropSiteListEntry)); |
| |
| if (entry == NULL) { |
| return False; |
| } |
| |
| sites = malloc(sizeof(Window)); |
| |
| if (sites == NULL) { |
| free(entry); |
| return False; |
| } |
| |
| sites[0] = clientHandle; |
| |
| entry->toplevel = serverHandle; |
| entry->root = None; |
| entry->event_mask = 0; |
| entry->embedded_sites_count = 1; |
| entry->embedded_sites = sites; |
| entry->next = embedded_drop_site_list; |
| embedded_drop_site_list = entry; |
| } |
| |
| return True; |
| } |
| |
| Boolean |
| unregister_xembed_drop_site(JNIEnv* env, Display* dpy, jobject server, |
| Window serverHandle, Window clientHandle) { |
| remove_from_embedded_drop_site_list(dpy, serverHandle, clientHandle); |
| return True; |
| } |
| |
| void |
| forward_event_to_embedded(Window embedded, jlong ctxt, jint eventID) { |
| static XClientMessageEvent* prevMessage = NULL; |
| static Boolean overXEmbedClient = False; |
| |
| XClientMessageEvent* xclient = |
| (XClientMessageEvent*)jlong_to_ptr(ctxt); |
| |
| if (xclient == NULL && prevMessage == NULL) { |
| return; |
| } |
| |
| if (xclient != NULL) { |
| /* |
| * NOTE: this check guarantees that prevMessage will always be an XDnD |
| * drag message. |
| */ |
| if (!is_xdnd_drag_message_type(xclient->message_type)) { |
| return; |
| } |
| |
| if (!overXEmbedClient) { |
| long* appended_data = jlong_to_ptr(ctxt) + |
| sizeof(XClientMessageEvent); |
| |
| /* Copy XdndTypeList from source to proxy. */ |
| if ((appended_data[0] & XDND_DATA_TYPES_BIT) != 0) { |
| unsigned char ret; |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char *data; |
| |
| data = NULL; |
| ret = checked_XGetWindowProperty(xclient->display, |
| xclient->data.l[0], |
| XA_XdndTypeList, 0, 0xFFFF, |
| False, XA_ATOM, &type, &format, |
| &nitems, &after, &data); |
| |
| /* Ignore the source if the window is destroyed. */ |
| if (ret == BadWindow) { |
| return; |
| } |
| |
| if (ret == Success) { |
| if (type == XA_ATOM && format == 32) { |
| ret = checked_XChangeProperty(xclient->display, |
| xclient->window, |
| XA_XdndTypeList, XA_ATOM, |
| 32, PropModeReplace, data, |
| nitems); |
| } |
| |
| XFree(data); |
| } |
| } |
| |
| set_proxy_mode_source_window(xclient->data.l[0]); |
| |
| { |
| XClientMessageEvent enter; |
| enter.display = xclient->display; |
| enter.type = ClientMessage; |
| enter.window = embedded; |
| enter.format = 32; |
| enter.message_type = XA_XdndEnter; |
| |
| enter.data.l[0] = xclient->window; /* XID of the source window */ |
| enter.data.l[1] = appended_data[0]; |
| enter.data.l[2] = appended_data[1]; |
| enter.data.l[3] = appended_data[2]; |
| enter.data.l[4] = appended_data[3]; |
| |
| forward_client_message_to_toplevel(embedded, &enter); |
| } |
| |
| overXEmbedClient = True; |
| } |
| |
| /* Make a copy of the original event, since we are going to modify the |
| event while it still can be referenced from other Java events. */ |
| { |
| XClientMessageEvent copy; |
| memcpy(©, xclient, sizeof(XClientMessageEvent)); |
| copy.data.l[0] = xclient->window; |
| |
| forward_client_message_to_toplevel(embedded, ©); |
| } |
| } |
| |
| if (eventID == java_awt_event_MouseEvent_MOUSE_EXITED) { |
| if (overXEmbedClient) { |
| if (xclient != NULL || prevMessage != NULL) { |
| /* Last chance to send XdndLeave to the XEmbed client. */ |
| XClientMessageEvent leave; |
| |
| leave.display = xclient != NULL ? |
| xclient->display : prevMessage->display; |
| leave.type = ClientMessage; |
| leave.window = embedded; |
| leave.format = 32; |
| leave.message_type = XA_XdndLeave; |
| leave.data.l[0] = xclient != NULL ? |
| xclient->window : prevMessage->window; /* XID of the source window */ |
| leave.data.l[1] = 0; /* flags */ |
| |
| forward_client_message_to_toplevel(embedded, &leave); |
| } |
| overXEmbedClient = False; |
| } |
| } |
| |
| if (eventID == java_awt_event_MouseEvent_MOUSE_RELEASED) { |
| overXEmbedClient = False; |
| awt_dnd_cleanup(); |
| } |
| |
| if (prevMessage != 0) { |
| free(prevMessage); |
| prevMessage = 0; |
| } |
| |
| if (xclient != 0 && overXEmbedClient) { |
| prevMessage = malloc(sizeof(XClientMessageEvent)); |
| |
| memcpy(prevMessage, xclient, sizeof(XClientMessageEvent)); |
| } |
| } |
| |
| /******************************************************************************/ |
| |
| /* |
| * Class: sun_awt_motif_MWindowPeer |
| * Method: registerX11DropTarget |
| * Signature: (Ljava/awt/Component;)V |
| */ |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_MWindowPeer_registerX11DropTarget(JNIEnv *env, jobject this, |
| jobject target) { |
| struct FrameData* wdata = NULL; |
| DropSitePtr dsi = NULL; |
| |
| wdata = (struct FrameData *) |
| JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData); |
| |
| if (wdata == NULL || wdata->winData.comp.widget == NULL) { |
| JNU_ThrowNullPointerException(env, "NULL component data"); |
| return; |
| } |
| |
| if (wdata->winData.shell == NULL) { |
| JNU_ThrowNullPointerException(env, "Null shell widget"); |
| return; |
| } |
| |
| DASSERT(wdata->winData.comp.dsi == NULL); |
| |
| dsi = (DropSitePtr)calloc(1, sizeof(struct DropSiteInfo)); |
| |
| if (dsi == NULL) { |
| JNU_ThrowOutOfMemoryError(env, ""); |
| return; |
| } |
| |
| dsi->component = (*env)->NewGlobalRef(env, target); |
| dsi->isComposite = False; |
| |
| wdata->winData.comp.dsi = dsi; |
| |
| AWT_LOCK(); |
| |
| awt_dnd_register_drop_site(wdata->winData.comp.widget, |
| dsi->component); |
| |
| AWT_UNLOCK(); |
| } |
| |
| /* |
| * Class: sun_awt_motif_MWindowPeer |
| * Method: unregisterX11DropTarget |
| * Signature: (Ljava/awt/Component;)V |
| */ |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_MWindowPeer_unregisterX11DropTarget(JNIEnv *env, |
| jobject this, |
| jobject target) { |
| struct FrameData* wdata = NULL; |
| DropSitePtr dsi = NULL; |
| |
| wdata = (struct FrameData *) |
| JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData); |
| |
| if (wdata == NULL) { |
| JNU_ThrowNullPointerException(env, "Null component data"); |
| return; |
| } |
| |
| if (wdata->winData.shell == NULL) { |
| JNU_ThrowNullPointerException(env, "Null shell widget"); |
| return; |
| } |
| |
| dsi = wdata->winData.comp.dsi; |
| |
| if (dsi == NULL) { |
| JNU_ThrowNullPointerException(env, "Null DropSiteInfo"); |
| return; |
| } |
| |
| AWT_LOCK(); |
| |
| awt_dnd_unregister_drop_site(wdata->winData.comp.widget, dsi->component); |
| |
| AWT_UNLOCK(); |
| |
| wdata->winData.comp.dsi = NULL; |
| |
| (*env)->DeleteGlobalRef(env, dsi->component); |
| |
| free(dsi); |
| } |
| |
| static void |
| dt_send_event_to_source(XClientMessageEvent* xclient) { |
| /* Shortcut if the source is in the same JVM. */ |
| if (xclient->window == awt_dnd_ds_get_source_window()) { |
| awt_dnd_ds_process_event((XEvent*)xclient); |
| } else { |
| unsigned char ret; |
| |
| ret = checked_XSendEvent(xclient->display, xclient->window, False, |
| NoEventMask, (XEvent*)xclient); |
| |
| if (ret == BadWindow) { |
| DTRACE_PRINTLN2("%s:%d XSendEvent - invalid window.", |
| __FILE__, __LINE__); |
| |
| /* Cleanup if we are still communicating with this window. */ |
| if (source_window == xclient->window) { |
| awt_dnd_cleanup(); |
| } |
| } |
| } |
| } |
| |
| static void |
| dt_send_response(XClientMessageEvent* xclient, jint eventID, jint action) { |
| Display* dpy = xclient->display; |
| XClientMessageEvent response; |
| |
| if (xclient->message_type == XA_XdndPosition) { |
| long* event_data = xclient->data.l; |
| |
| if (eventID == java_awt_event_MouseEvent_MOUSE_EXITED) { |
| action = java_awt_dnd_DnDConstants_ACTION_NONE; |
| } |
| |
| response.display = dpy; |
| response.type = ClientMessage; |
| response.window = event_data[0]; |
| response.format = 32; |
| response.message_type = XA_XdndStatus; |
| /* target window */ |
| response.data.l[0] = xclient->window; |
| /* flags */ |
| response.data.l[1] = 0; |
| if (action != java_awt_dnd_DnDConstants_ACTION_NONE) { |
| response.data.l[1] |= XDND_ACCEPT_DROP_FLAG; |
| } |
| /* specify an empty rectangle */ |
| response.data.l[2] = 0; /* x, y */ |
| response.data.l[3] = 0; /* w, h */ |
| /* action accepted by the target */ |
| response.data.l[4] = java_to_xdnd_action(action); |
| } else if (xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) { |
| int reason = (int)(xclient->data.b[0] & MOTIF_MESSAGE_REASON_MASK); |
| int origin = (int)(xclient->data.b[0] & MOTIF_MESSAGE_SENDER_MASK); |
| unsigned char byte_order = xclient->data.b[1]; |
| CARD16 response_flags = 0; |
| CARD8 response_reason = 0; |
| void* p = &response.data.b; |
| |
| /* Only initiator messages should be handled. */ |
| if (origin != MOTIF_MESSAGE_FROM_INITIATOR) { |
| DTRACE_PRINTLN2("%s:%d Receiver message.", __FILE__, __LINE__); |
| return; |
| } |
| |
| switch (reason) { |
| case DRAG_MOTION: |
| switch (eventID) { |
| case java_awt_event_MouseEvent_MOUSE_ENTERED: |
| response_reason = DROP_SITE_ENTER; |
| break; |
| case java_awt_event_MouseEvent_MOUSE_DRAGGED: |
| response_reason = DRAG_MOTION; |
| break; |
| case java_awt_event_MouseEvent_MOUSE_EXITED: |
| response_reason = DROP_SITE_LEAVE; |
| break; |
| } |
| } |
| |
| response.display = dpy; |
| response.type = ClientMessage; |
| response.window = read_card32(xclient->data.b, 12, byte_order); |
| response.format = 8; |
| response.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE; |
| |
| write_card8(&p, response_reason | MOTIF_MESSAGE_FROM_RECEIVER); |
| write_card8(&p, MOTIF_BYTE_ORDER); |
| |
| if (response_reason != DROP_SITE_LEAVE) { |
| CARD16 flags = read_card16(xclient->data.b, 2, byte_order); |
| unsigned char drop_site_status = |
| (action == java_awt_dnd_DnDConstants_ACTION_NONE) ? |
| MOTIF_INVALID_DROP_SITE : MOTIF_VALID_DROP_SITE; |
| |
| /* Clear action and drop site status bits. */ |
| response_flags = |
| flags & ~MOTIF_DND_ACTION_MASK & ~MOTIF_DND_STATUS_MASK; |
| |
| /* Fill in new action and drop site status. */ |
| response_flags |= |
| java_to_motif_actions(action) << MOTIF_DND_ACTION_SHIFT; |
| response_flags |= |
| drop_site_status << MOTIF_DND_STATUS_SHIFT; |
| } else { |
| response_flags = 0; |
| } |
| |
| write_card16(&p, response_flags); |
| |
| /* Write time stamp. */ |
| write_card32(&p, read_card32(xclient->data.b, 4, byte_order)); |
| |
| /* Write coordinates. */ |
| if (response_reason != DROP_SITE_LEAVE) { |
| write_card16(&p, read_card16(xclient->data.b, 8, byte_order)); |
| write_card16(&p, read_card16(xclient->data.b, 10, byte_order)); |
| } else { |
| write_card16(&p, 0); |
| write_card16(&p, 0); |
| } |
| } else { |
| return; |
| } |
| |
| dt_send_event_to_source(&response); |
| } |
| |
| static void |
| dummy_selection_callback(Widget w, XtPointer client_data, Atom* selection, |
| Atom* type, XtPointer value, unsigned long *length, |
| int32_t *format) { |
| /* The selection callback is responsible for freeing the data. */ |
| if (value != NULL) { |
| XtFree(value); |
| value = NULL; |
| } |
| } |
| |
| static void |
| dt_notify_drop_done(JNIEnv* env, XClientMessageEvent* xclient, jboolean success, |
| jint action) { |
| if (xclient->message_type == XA_XdndDrop) { |
| Display* dpy = xclient->display; |
| XClientMessageEvent finished; |
| long* event_data = xclient->data.l; |
| |
| /* |
| * The XDnD protocol recommends that the target requests the special |
| * target DELETE in case if the drop action is XdndActionMove. |
| */ |
| if (action == java_awt_dnd_DnDConstants_ACTION_MOVE && |
| success == JNI_TRUE) { |
| |
| Time time_stamp = event_data[2]; |
| |
| XtGetSelectionValue(awt_root_shell, XA_XdndSelection, XA_DELETE, |
| dummy_selection_callback, NULL, time_stamp); |
| } |
| |
| finished.display = dpy; |
| finished.type = ClientMessage; |
| finished.window = event_data[0]; |
| finished.format = 32; |
| finished.message_type = XA_XdndFinished; |
| finished.data.l[0] = xclient->window; |
| finished.data.l[1] = 0; /* flags */ |
| finished.data.l[2] = None; |
| if (source_protocol_version >= 5) { |
| if (success == JNI_TRUE) { |
| finished.data.l[1] |= XDND_ACCEPT_DROP_FLAG; |
| } |
| finished.data.l[2] = java_to_xdnd_action(action); |
| } |
| |
| dt_send_event_to_source(&finished); |
| } else if (xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) { |
| char* event_data = xclient->data.b; |
| unsigned char event_byte_order = read_card8(event_data, 1); |
| unsigned char first_byte = read_card8(event_data, 0); |
| unsigned char reason = first_byte & MOTIF_MESSAGE_REASON_MASK; |
| unsigned char origin = first_byte & MOTIF_MESSAGE_SENDER_MASK; |
| Atom selection = None; |
| Time time_stamp = CurrentTime; |
| Atom status_atom = None; |
| |
| if (origin != MOTIF_MESSAGE_FROM_INITIATOR) { |
| DTRACE_PRINTLN2("%s:%d Invalid origin.", __FILE__, __LINE__); |
| return; |
| } |
| |
| if (reason != DROP_START) { |
| DTRACE_PRINTLN2("%s:%d Invalid reason.", __FILE__, __LINE__); |
| return; |
| } |
| |
| selection = read_card32(event_data, 12, event_byte_order); |
| time_stamp = read_card32(event_data, 4, event_byte_order); |
| |
| if (success == JNI_TRUE) { |
| status_atom = XA_XmTRANSFER_SUCCESS; |
| } else { |
| status_atom = XA_XmTRANSFER_FAILURE; |
| } |
| |
| /* |
| * This is just the way to communicate the drop completion status back |
| * to the initiator as prescribed by the Motif DnD protocol. |
| */ |
| XtGetSelectionValue(awt_root_shell, selection, status_atom, |
| dummy_selection_callback, NULL, time_stamp); |
| } |
| |
| /* |
| * Flush the buffer to guarantee that the drop completion event is sent |
| * to the source before the method returns. |
| */ |
| XFlush(awt_display); |
| |
| /* Trick to prevent awt_dnd_cleanup() from posting dragExit */ |
| target_component = NULL; |
| /* Cannot do cleanup before the drop finishes as we need source protocol |
| version to send XdndFinished message. */ |
| awt_dnd_cleanup(); |
| } |
| |
| /* |
| * Class: sun_awt_motif_X11DropTargetContextPeer |
| * Method: sendResponse |
| * Signature: (IIJZ)V |
| */ |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_X11DropTargetContextPeer_sendResponse(JNIEnv *env, |
| jobject this, |
| jint eventID, |
| jint action, |
| jlong nativeCtxt, |
| jboolean dispatcherDone, |
| jboolean consumed) { |
| XClientMessageEvent* xclient = |
| (XClientMessageEvent*)jlong_to_ptr(nativeCtxt); |
| |
| AWT_LOCK(); |
| |
| if (consumed == JNI_FALSE) { |
| dt_send_response(xclient, eventID, action); |
| } |
| |
| /* |
| * Free the native context only if all copies of the original event are |
| * processed. |
| */ |
| if (dispatcherDone == JNI_TRUE) { |
| XtFree((char*)xclient); |
| } |
| |
| AWT_UNLOCK(); |
| } |
| |
| /* |
| * Class: sun_awt_motif_X11DropTargetContextPeer |
| * Method: dropDone |
| * Signature: (JZI)V |
| */ |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_X11DropTargetContextPeer_dropDone(JNIEnv *env, |
| jobject this, |
| jlong nativeCtxt, |
| jboolean success, |
| jint action) { |
| XClientMessageEvent* xclient = |
| (XClientMessageEvent*)jlong_to_ptr(nativeCtxt); |
| |
| AWT_LOCK(); |
| |
| dt_notify_drop_done(env, xclient, success, action); |
| |
| XtFree((char*)xclient); |
| |
| AWT_UNLOCK(); |
| } |
| |
| /* |
| * Class: sun_awt_motif_X11DropTargetContextPeer |
| * Method: getData |
| * Signature: (IJ)Ljava/lang/Object; |
| */ |
| |
| JNIEXPORT jobject JNICALL |
| Java_sun_awt_motif_X11DropTargetContextPeer_getData(JNIEnv *env, |
| jobject this, |
| jlong nativeCtxt, |
| jlong formatAtom) { |
| XClientMessageEvent* xclient = |
| (XClientMessageEvent*)jlong_to_ptr(nativeCtxt); |
| |
| Atom selection = None; |
| Time time_stamp = CurrentTime; |
| Atom target = (Atom)formatAtom; |
| |
| if (xclient->message_type == XA_XdndDrop || |
| xclient->message_type == XA_XdndPosition) { |
| Display* dpy = xclient->display; |
| Window source_win = xclient->data.l[0]; |
| Atom protocol_version = 0; |
| |
| int status; |
| |
| Atom type; |
| int format; |
| unsigned long nitems; |
| unsigned long after; |
| unsigned char *data; |
| |
| AWT_LOCK(); |
| |
| data = NULL; |
| status = XGetWindowProperty(dpy, source_win, XA_XdndAware, 0, 0xFFFF, |
| False, XA_ATOM, &type, &format, &nitems, |
| &after, &data); |
| |
| if (status == Success && data != NULL && type == XA_ATOM && format == 32 |
| && nitems > 0) { |
| protocol_version = (protocol_version > XDND_PROTOCOL_VERSION) ? |
| XDND_PROTOCOL_VERSION : protocol_version; |
| |
| if (protocol_version > 0) { |
| if (xclient->message_type == XA_XdndDrop) { |
| time_stamp = xclient->data.l[2]; |
| } else if (xclient->message_type == XA_XdndPosition) { |
| time_stamp = xclient->data.l[3]; |
| } |
| } |
| } |
| |
| if (status == Success) { |
| XFree(data); |
| data = NULL; |
| } |
| |
| AWT_FLUSH_UNLOCK(); |
| |
| selection = XA_XdndSelection; |
| if (time_stamp == CurrentTime) { |
| time_stamp = awt_util_getCurrentServerTime(); |
| } |
| |
| } else if (xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) { |
| char* event_data = xclient->data.b; |
| unsigned char event_byte_order = read_card8(event_data, 1); |
| unsigned char first_byte = read_card8(event_data, 0); |
| unsigned char reason = first_byte & MOTIF_MESSAGE_REASON_MASK; |
| unsigned char origin = first_byte & MOTIF_MESSAGE_SENDER_MASK; |
| |
| if (origin != MOTIF_MESSAGE_FROM_INITIATOR) { |
| DTRACE_PRINTLN2("%s:%d Invalid origin.", __FILE__, __LINE__); |
| return NULL; |
| } |
| |
| switch (reason) { |
| case DROP_START: |
| selection = read_card32(event_data, 12, event_byte_order); |
| break; |
| case DRAG_MOTION: |
| case OPERATION_CHANGED: |
| selection = source_atom; |
| break; |
| default: |
| DTRACE_PRINTLN2("%s:%d Invalid reason.", __FILE__, __LINE__); |
| return NULL; |
| } |
| |
| if (selection == None) { |
| return NULL; |
| } |
| |
| time_stamp = read_card32(event_data, 4, event_byte_order); |
| } else { |
| return NULL; |
| } |
| |
| return get_selection_data(env, selection, target, time_stamp); |
| } |