blob: 7b76f81195e53c43bef03dc3f419396d1a0909c9 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26#ifdef HEADLESS
27 #error This file should not be included in headless library
28#endif
29
30#include "awt_dnd.h"
31
32#include "jlong.h"
33
34#include "awt_DataTransferer.h"
35#include "awt_MToolkit.h"
36
37#include "java_awt_dnd_DnDConstants.h"
38#include "java_awt_event_MouseEvent.h"
39
40#include "sun_awt_motif_MComponentPeer.h"
41#include "awt_xembed.h"
42
43#define DT_INITIAL_STATE 0
44#define DT_ENTERED_STATE 1
45#define DT_OVER_STATE 2
46
47extern struct ComponentIDs componentIDs;
48extern struct MComponentPeerIDs mComponentPeerIDs;
49
50/**************************** XEmbed server DnD support ***********************/
51extern void
52set_xembed_drop_target(JNIEnv* env, jobject server);
53extern void
54remove_xembed_drop_target(JNIEnv* env, jobject server);
55extern Boolean
56is_xembed_client(Window window);
57
58DECLARE_JAVA_CLASS(MEmbedCanvasPeerClass, "sun/awt/motif/MEmbedCanvasPeer");
59/******************************************************************************/
60
61typedef enum {
62 EventSuccess, /* Event is successfully processed. */
63 EventFailure /* Failed to process the event. */
64} EventStatus;
65
66typedef enum {
67 EnterEvent, /* XdndEnter, TOP_LEVEL_ENTER */
68 MotionEvent, /* XdndPosition, DRAG_MOTION, OPERATION_CHANGED */
69 LeaveEvent, /* XdndLeave, TOP_LEVEL_LEAVE */
70 DropEvent, /* XdndDrop, DROP_START */
71 UnknownEvent
72} EventType;
73
74static Protocol source_protocol = NO_PROTOCOL;
75static unsigned int source_protocol_version = 0;
76static Window source_window = None;
77static Atom source_atom = None;
78static long source_window_mask = None;
79static jint source_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
80/*
81 * According to XDnD protocol, XdndActionList is optional.
82 * In case if XdndActionList is not set on the source, the list of drop actions
83 * supported by the source is constructed as follows:
84 * - "copy" is always included;
85 * - "move" is included if at least one XdndPosition message received
86 * after the latest XdndEnter passed XdndActionMove in data.l[4];
87 * - "link" is included if at least one XdndPosition message received
88 * after the latest XdndEnter passed XdndActionLink in data.l[4].
89 * We use a boolean flag to signal that we are building the list of drop actions
90 * supported by the source.
91 */
92static Boolean track_source_actions = False;
93static jint user_action = java_awt_dnd_DnDConstants_ACTION_NONE;
94static jlongArray source_data_types = NULL;
95static Atom* source_data_types_native = NULL;
96static unsigned int source_data_types_count = 0;
97static int source_x = 0;
98static int source_y = 0;
99static jobject target_component = NULL;
100/*
101 * The Motif DnD protocol prescribes that DROP_START message should always be
102 * preceeded with TOP_LEVEL_LEAVE message. We need to cleanup on TOP_LEVEL_LEAVE
103 * message, but DROP_START wouldn't be processed properly.
104 * To resolve this issue we postpone cleanup using a boolean flag this flag is
105 * set when we receive the TOP_LEVEL_LEAVE message and cleared when the next
106 * client message arrives if that message is not DROP_START. If that message is
107 * a DROP_START message, the flag is cleared after the DROP_START is processed.
108 */
109static Boolean motif_top_level_leave_postponed = False;
110/*
111 * We store a postponed TOP_LEVEL_LEAVE message here.
112 */
113static XClientMessageEvent motif_top_level_leave_postponed_event;
114
115/* Forward declarations */
116static Window get_root_for_window(Window window);
117static Window get_outer_canvas_for_window(Window window);
118static Boolean register_drop_site(Widget outer_canvas, XtPointer componentRef);
119static Boolean is_xdnd_drag_message_type(unsigned long message_type);
120static Boolean register_xdnd_drop_site(Display* dpy, Window toplevel,
121 Window window);
122
123/**************************** JNI stuff ***************************************/
124
125DECLARE_JAVA_CLASS(dtcp_clazz, "sun/awt/motif/X11DropTargetContextPeer")
126
127static void
128dt_postDropTargetEvent(JNIEnv* env, jobject component, int x, int y,
129 jint dropAction, jint event_id,
130 XClientMessageEvent* event) {
131 DECLARE_STATIC_VOID_JAVA_METHOD(dtcp_postDropTargetEventToPeer, dtcp_clazz,
132 "postDropTargetEventToPeer",
133 "(Ljava/awt/Component;IIII[JJI)V");
134
135 {
136 void* copy = NULL;
137
138 if (event != NULL) {
139 /*
140 * For XDnD messages we append the information from the latest
141 * XdndEnter to the context. It is done to be able to reconstruct
142 * XdndEnter for an XEmbed client.
143 */
144 Boolean isXDnDMessage =
145 is_xdnd_drag_message_type(event->message_type);
146
147 if (isXDnDMessage) {
148 copy = malloc(sizeof(XClientMessageEvent) +
149 4 * sizeof(long));
150 } else {
151 copy = malloc(sizeof(XClientMessageEvent));
152 }
153
154 if (copy == NULL) {
155 DTRACE_PRINTLN2("%s:%d malloc failed.", __FILE__, __LINE__);
156 return;
157 }
158
159 memcpy(copy, event, sizeof(XClientMessageEvent));
160
161 if (isXDnDMessage) {
162 size_t msgSize = sizeof(XClientMessageEvent);
163 long data1 = source_protocol_version << XDND_PROTOCOL_SHIFT;
164 long * appended_data;
165 if (source_data_types_native != NULL &&
166 source_data_types_count > 3) {
167
168 data1 |= XDND_DATA_TYPES_BIT;
169 }
170
171 appended_data = (long*)((char*)copy + msgSize);
172 appended_data[0] = data1;
173 appended_data[1] = source_data_types_count > 0 ?
174 source_data_types_native[0] : 0;
175 appended_data[2] = source_data_types_count > 1 ?
176 source_data_types_native[1] : 0;
177 appended_data[3] = source_data_types_count > 2 ?
178 source_data_types_native[2] : 0;
179 }
180 }
181
182 DASSERT(!JNU_IsNull(env, component));
183
184 (*env)->CallStaticVoidMethod(env, clazz, dtcp_postDropTargetEventToPeer,
185 component, x, y, dropAction,
186 source_actions, source_data_types,
187 ptr_to_jlong(copy), event_id);
188 }
189}
190
191/******************************************************************************/
192
193/********************* Embedded drop site list support ************************/
194
195struct EmbeddedDropSiteListEntryRec;
196
197typedef struct EmbeddedDropSiteListEntryRec EmbeddedDropSiteListEntry;
198
199struct EmbeddedDropSiteListEntryRec {
200 Window toplevel;
201 Window root;
202 /*
203 * We select for PropertyNotify events on the toplevel, so we need to
204 * restore the event mask when we are done with this toplevel.
205 */
206 long event_mask;
207 unsigned int embedded_sites_count;
208 Window* embedded_sites;
209 EmbeddedDropSiteListEntry* next;
210};
211
212static EmbeddedDropSiteListEntry* embedded_drop_site_list = NULL;
213
214struct EmbeddedDropSiteProtocolListEntryRec;
215
216typedef struct EmbeddedDropSiteProtocolListEntryRec EmbeddedDropSiteProtocolListEntry;
217
218struct EmbeddedDropSiteProtocolListEntryRec {
219 Window window;
220 Window proxy;
221 /*
222 * We override the XdndAware property on the toplevel, so we should keep its
223 * original contents - the XDnD protocol version supported by the browser.
224 * This is needed to adjust XDnD messages forwarded to the browser.
225 */
226 unsigned int protocol_version;
227 /* True if the toplevel was already registered as a drag receiver and
228 we just changed the proxy. False, otherwise */
229 Boolean overriden;
230 EmbeddedDropSiteProtocolListEntry* next;
231};
232
233static EmbeddedDropSiteProtocolListEntry* embedded_motif_protocol_list = NULL;
234static EmbeddedDropSiteProtocolListEntry* embedded_xdnd_protocol_list = NULL;
235
236typedef enum {
237 RegFailure, /* Proxy registration failed */
238 RegSuccess, /* The new drop site is registered with the new proxy */
239 RegOverride, /* The new proxy is set for the existing drop site */
240 RegAlreadyRegistered /* This proxy is already set for this drop site */
241} ProxyRegistrationStatus;
242
243/* Forward declarations. */
244static EmbeddedDropSiteProtocolListEntry*
245get_xdnd_protocol_entry_for_toplevel(Window toplevel);
246static EmbeddedDropSiteProtocolListEntry*
247get_motif_protocol_entry_for_toplevel(Window toplevel);
248static void remove_xdnd_protocol_entry_for_toplevel(Window toplevel);
249static void remove_motif_protocol_entry_for_toplevel(Window toplevel);
250
251/*
252 * Registers the toplevel as a Motif drag receiver if it is not registered yet,
253 * sets the specified new_proxy for it and returns the previous proxy in old_proxy.
254 * Does nothing if the new_proxy is already set as a proxy for this toplevel.
255 * Returns the completion status.
256 */
257static ProxyRegistrationStatus
258set_motif_proxy(Display* dpy, Window toplevel, Window new_proxy, Window *old_proxy) {
259 Boolean override = False;
260
261 Atom type;
262 int format;
263 unsigned long nitems;
264 unsigned long after;
265 unsigned char* data;
266 unsigned char ret;
267
268 DASSERT(old_proxy != NULL);
269
270 *old_proxy = None;
271
272 data = NULL;
273 ret = checked_XGetWindowProperty(dpy, toplevel,
274 _XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF,
275 False, AnyPropertyType, &type, &format,
276 &nitems, &after, &data);
277
278 /* Check if toplevel is a valid window. */
279 if (ret != Success) {
280 return RegFailure;
281 }
282
283 if (ret == Success && data != NULL && type != None && format == 8
284 && nitems >= MOTIF_RECEIVER_INFO_SIZE) {
285 unsigned char byte_order = read_card8((char*)data, 0);
286 void* p = (char*)data + 4;
287
288 /* Browser and plugin have different byte orders - report failure for now. */
289 if (MOTIF_BYTE_ORDER != byte_order) {
290 XFree(data);
291 return RegFailure;
292 }
293
294 *old_proxy = read_card32((char*)data, 4, byte_order);
295
296 /* If the proxy is already set to the specified window - return. */
297 if (*old_proxy == new_proxy) {
298 XFree(data);
299 return RegAlreadyRegistered;
300 }
301
302 /* replace the proxy window */
303 write_card32(&p, new_proxy);
304
305 override = True;
306 } else {
307 void* p;
308
309 if (ret == Success) {
310 XFree(data);
311 data = NULL;
312 }
313
314 data = malloc(MOTIF_RECEIVER_INFO_SIZE);
315
316 if (data == NULL) {
317 DTRACE_PRINTLN2("%s:%d malloc failed.", __FILE__, __LINE__);
318 return RegFailure;
319 }
320
321 p = data;
322
323 write_card8(&p, MOTIF_BYTE_ORDER);
324 write_card8(&p, MOTIF_DND_PROTOCOL_VERSION); /* protocol version */
325 write_card8(&p, MOTIF_DYNAMIC_STYLE); /* protocol style */
326 write_card8(&p, 0); /* pad */
327 write_card32(&p, new_proxy); /* proxy window */
328 write_card16(&p, 0); /* num_drop_sites */
329 write_card16(&p, 0); /* pad */
330 write_card32(&p, MOTIF_RECEIVER_INFO_SIZE);
331 }
332
333 ret = checked_XChangeProperty(dpy, toplevel,
334 _XA_MOTIF_DRAG_RECEIVER_INFO,
335 _XA_MOTIF_DRAG_RECEIVER_INFO, 8,
336 PropModeReplace, (unsigned char*)data,
337 MOTIF_RECEIVER_INFO_SIZE);
338
339 if (data != NULL) {
340 XFree(data);
341 data = NULL;
342 }
343
344 if (ret == Success) {
345 if (override) {
346 return RegOverride;
347 } else {
348 return RegSuccess;
349 }
350 } else {
351 return RegFailure;
352 }
353}
354
355/*
356 * Registers the toplevel as a XDnD drag receiver if it is not registered yet,
357 * sets the specified new_proxy for it and returns the previous proxy in
358 * old_proxy and the original XDnD protocol version in old_version.
359 * Does nothing if the new_proxy is already set as a proxy for this toplevel.
360 * Returns the completion status.
361 */
362static ProxyRegistrationStatus
363set_xdnd_proxy(Display* dpy, Window toplevel, Window new_proxy,
364 Window* old_proxy, unsigned int* old_version) {
365 Atom version_atom = XDND_PROTOCOL_VERSION;
366 Window xdnd_proxy = None;
367 Boolean override = False;
368
369 Atom type;
370 int format;
371 unsigned long nitems;
372 unsigned long after;
373 unsigned char* data;
374 unsigned char ret;
375
376 DASSERT(old_proxy != NULL);
377
378 *old_proxy = None;
379
380 data = NULL;
381 ret = checked_XGetWindowProperty(dpy, toplevel, XA_XdndAware, 0, 1,
382 False, AnyPropertyType, &type, &format,
383 &nitems, &after, &data);
384
385 if (ret != Success) {
386 return RegFailure;
387 }
388
389 if (ret == Success && data != NULL && type == XA_ATOM) {
390 unsigned int protocol_version = *((unsigned int*)data);
391
392 override = True;
393 *old_version = protocol_version;
394
395 /* XdndProxy is not supported for prior to XDnD version 4 */
396 if (protocol_version >= 4) {
397 int status;
398
399 XFree(data);
400
401 data = NULL;
402 status = XGetWindowProperty(dpy, toplevel, XA_XdndProxy, 0, 1,
403 False, XA_WINDOW, &type, &format,
404 &nitems, &after, &data);
405
406 if (status == Success && data != NULL && type == XA_WINDOW) {
407 xdnd_proxy = *((Window*)data);
408
409 if (xdnd_proxy != None) {
410 XFree(data);
411
412 data = NULL;
413 status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndProxy,
414 0, 1, False, XA_WINDOW, &type,
415 &format, &nitems, &after, &data);
416
417 if (status != Success || data == NULL || type != XA_WINDOW ||
418 *((Window*)data) != xdnd_proxy) {
419 /* Ignore invalid proxy. */
420 xdnd_proxy = None;
421 }
422 }
423
424 if (xdnd_proxy != None) {
425 XFree(data);
426
427 data = NULL;
428 status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndAware,
429 0, 1, False, AnyPropertyType,
430 &type, &format, &nitems, &after,
431 &data);
432
433 if (status == Success && data != NULL && type == XA_ATOM) {
434 unsigned int proxy_version = *((unsigned int*)data);
435
436 if (proxy_version != protocol_version) {
437 /* Ignore invalid proxy. */
438 xdnd_proxy = None;
439 }
440 } else {
441 /* Ignore invalid proxy. */
442 xdnd_proxy = None;
443 }
444 }
445 }
446
447 *old_proxy = xdnd_proxy;
448 }
449 }
450
451 XFree(data);
452
453 /* If the proxy is already set to the specified window - return. */
454 if (xdnd_proxy == new_proxy) {
455 return RegAlreadyRegistered;
456 }
457
458 /* The proxy window must have the XdndAware set, as XDnD protocol prescribes
459 to check the proxy window for XdndAware. */
460 ret = checked_XChangeProperty(dpy, new_proxy, XA_XdndAware, XA_ATOM, 32,
461 PropModeReplace,
462 (unsigned char*)&version_atom, 1);
463
464 if (ret != Success) {
465 return RegFailure;
466 }
467
468 /* The proxy window must have the XdndProxy set to point to itself. */
469 ret = checked_XChangeProperty(dpy, new_proxy, XA_XdndProxy, XA_WINDOW, 32,
470 PropModeReplace,
471 (unsigned char*)&new_proxy, 1);
472
473 if (ret != Success) {
474 return RegFailure;
475 }
476
477 ret = checked_XChangeProperty(dpy, toplevel, XA_XdndAware, XA_ATOM, 32,
478 PropModeReplace,
479 (unsigned char*)&version_atom, 1);
480
481 if (ret != Success) {
482 return RegFailure;
483 }
484
485 ret = checked_XChangeProperty(dpy, toplevel, XA_XdndProxy, XA_WINDOW, 32,
486 PropModeReplace,
487 (unsigned char*)&new_proxy, 1);
488
489 if (ret == Success) {
490 if (override) {
491 return RegOverride;
492 } else {
493 return RegSuccess;
494 }
495 } else {
496 return RegFailure;
497 }
498}
499
500/*
501 * 'toplevel' is the browser toplevel window. To register a drop site on the
502 * plugin window we set the proxy for the browser toplevel window to point to
503 * the awt_root_shell window.
504 *
505 * We assume that only one JVM per browser instance is possible. This
506 * assumption is true with the current plugin implementation - it creates a
507 * single JVM for all plugin instances created by the given plugin factory.
508 *
509 * When a client message event for the browser toplevel window is received, we
510 * will iterate over drop sites registered with this toplevel and determine if
511 * the mouse pointer is currently over one of them (there could be several
512 * plugin windows in one browser window - for example if an HTML page contains
513 * frames and several frames contain a plugin object).
514 *
515 * If the pointer is not over any of the plugin drop sites the client message
516 * will be resent to the browser, otherwise it will be processed normally.
517 */
518static EmbeddedDropSiteListEntry*
519awt_dnd_dt_init_proxy(Display* dpy, Window root, Window toplevel, Window window) {
520 Window awt_root_window = get_awt_root_window();
521 Window motif_proxy = None;
522 Boolean motif_override = False;
523 unsigned long event_mask = 0;
524
525 if (awt_root_window == None) {
526 return NULL;
527 }
528
529 /* Grab server, since we are working with the window that belongs to
530 another client. REMIND: ungrab when done!!! */
531 XGrabServer(dpy);
532
533 {
534 ProxyRegistrationStatus motif_status = RegFailure;
535
536 motif_status = set_motif_proxy(dpy, toplevel, awt_root_window, &motif_proxy);
537
538 switch (motif_status) {
539 case RegFailure:
540 case RegAlreadyRegistered:
541 XUngrabServer(dpy);
542 /* Workaround for bug 5039226 */
543 XSync(dpy, False);
544 return NULL;
545 case RegOverride:
546 motif_override = True;
547 break;
548 case RegSuccess:
549 motif_override = False;
550 break;
551 default:
552 DASSERT(False);
553 }
554
555
556 }
557
558 {
559 XWindowAttributes xwa;
560 XGetWindowAttributes(dpy, toplevel, &xwa);
561 event_mask = xwa.your_event_mask;
562 if ((event_mask & PropertyChangeMask) == 0) {
563 XSelectInput(dpy, toplevel, event_mask | PropertyChangeMask);
564 }
565 }
566
567 XUngrabServer(dpy);
568 /* Workaround for bug 5039226 */
569 XSync(dpy, False);
570
571 /* Add protocol specific entries for the toplevel. */
572 {
573 EmbeddedDropSiteProtocolListEntry* motif_entry = NULL;
574
575 motif_entry = malloc(sizeof(EmbeddedDropSiteProtocolListEntry));
576
577 if (motif_entry == NULL) {
578 return NULL;
579 }
580
581 motif_entry->window = toplevel;
582 motif_entry->proxy = motif_proxy;
583 motif_entry->protocol_version = 0;
584 motif_entry->overriden = motif_override;
585 motif_entry->next = embedded_motif_protocol_list;
586 embedded_motif_protocol_list = motif_entry;
587 }
588
589 {
590 EmbeddedDropSiteListEntry* entry = NULL;
591 Window* sites = NULL;
592
593 entry = malloc(sizeof(EmbeddedDropSiteListEntry));
594
595 if (entry == NULL) {
596 return NULL;
597 }
598
599 sites = malloc(sizeof(Window));
600
601 if (sites == NULL) {
602 free(entry);
603 return NULL;
604 }
605
606 sites[0] = window;
607
608 entry->toplevel = toplevel;
609 entry->root = root;
610 entry->event_mask = event_mask;
611 entry->embedded_sites_count = 1;
612 entry->embedded_sites = sites;
613 entry->next = NULL;
614
615 return entry;
616 }
617}
618
619static void
620register_xdnd_embedder(Display* dpy, EmbeddedDropSiteListEntry* entry, long window) {
621 Window awt_root_window = get_awt_root_window();
622 Window toplevel = entry->toplevel;
623 Window xdnd_proxy = None;
624 unsigned int xdnd_protocol_version = 0;
625 Boolean xdnd_override = False;
626 Boolean register_xdnd = True;
627 Boolean motif_overriden = False;
628
629 EmbeddedDropSiteProtocolListEntry* motif_entry = embedded_motif_protocol_list;
630 while (motif_entry != NULL) {
631 if (motif_entry->window == toplevel) {
632 motif_overriden = motif_entry->overriden;
633 break;
634 }
635 motif_entry = motif_entry->next;
636 }
637
638 /*
639 * First check if the window is an XEmbed client.
640 * In this case we don't have to setup a proxy on the toplevel,
641 * instead we register the XDnD drop site on the embedded window.
642 */
643 if (isXEmbedActiveByWindow(window)) {
644 register_xdnd_drop_site(dpy, toplevel, window);
645 return;
646 }
647
648 /*
649 * By default, we register a drop site that supports both dnd
650 * protocols. This approach is not appropriate in plugin
651 * scenario if the browser doesn't support XDnD. If we forcibly set
652 * XdndAware on the browser toplevel, any drag source that supports both
653 * protocols and prefers XDnD will be unable to drop anything on the
654 * browser.
655 * The solution for this problem is not to register XDnD drop site
656 * if the browser supports only Motif DnD.
657 */
658 if (motif_overriden) {
659 int status;
660 Atom type;
661 int format;
662 unsigned long nitems;
663 unsigned long after;
664 unsigned char* data;
665
666 data = NULL;
667 status = XGetWindowProperty(dpy, toplevel, XA_XdndAware, 0, 1,
668 False, AnyPropertyType, &type, &format,
669 &nitems, &after, &data);
670
671 XFree(data);
672 data = NULL;
673
674 if (type != XA_ATOM) {
675 register_xdnd = False;
676 }
677 }
678
679 if (register_xdnd) {
680 ProxyRegistrationStatus xdnd_status;
681 /* Grab server, since we are working with the window that belongs to
682 another client. REMIND: ungrab when done!!! */
683 XGrabServer(dpy);
684
685 xdnd_status =
686 set_xdnd_proxy(dpy, toplevel, awt_root_window, &xdnd_proxy,
687 &xdnd_protocol_version);
688
689 XUngrabServer(dpy);
690
691 switch (xdnd_status) {
692 case RegFailure:
693 case RegAlreadyRegistered:
694 return;
695 case RegOverride:
696 xdnd_override = True;
697 break;
698 case RegSuccess:
699 xdnd_override = False;
700 break;
701 default:
702 DASSERT(False);
703 }
704
705 {
706 EmbeddedDropSiteProtocolListEntry* xdnd_entry = NULL;
707
708 xdnd_entry = malloc(sizeof(EmbeddedDropSiteProtocolListEntry));
709
710 if (xdnd_entry == NULL) {
711 return;
712 }
713
714 xdnd_entry->window = toplevel;
715 xdnd_entry->proxy = xdnd_proxy;
716 xdnd_entry->protocol_version = xdnd_protocol_version;
717 xdnd_entry->overriden = xdnd_override;
718 xdnd_entry->next = embedded_xdnd_protocol_list;
719 embedded_xdnd_protocol_list = xdnd_entry;
720 }
721 }
722}
723
724/*
725 * If embedded_drop_site_list already contains an entry with the specified
726 * 'toplevel', the method registers the specified 'window' as an embedded drop
727 * site for this 'toplevel' and returns True.
728 * Otherwise, it checks if the 'toplevel' is a registered drop site for adds
729 * (window, component) pair to the list and returns True
730 * if completes successfully.
731 */
732static Boolean
733add_to_embedded_drop_site_list(Display* dpy, Window root, Window toplevel,
734 Window window) {
735 EmbeddedDropSiteListEntry* entry = embedded_drop_site_list;
736
737 while (entry != NULL) {
738 if (entry->toplevel == toplevel) {
739 void* p = realloc(entry->embedded_sites,
740 sizeof(Window) *
741 (entry->embedded_sites_count + 1));
742 if (p == NULL) {
743 return False;
744 }
745 entry->embedded_sites = p;
746 entry->embedded_sites[entry->embedded_sites_count++] = window;
747
748 register_xdnd_embedder(dpy, entry, window);
749
750 return True;
751 }
752 entry = entry->next;
753 }
754
755 entry = awt_dnd_dt_init_proxy(dpy, root, toplevel, window);
756
757 if (entry == NULL) {
758 return False;
759 }
760
761 register_xdnd_embedder(dpy, entry, window);
762
763 entry->next = embedded_drop_site_list;
764 embedded_drop_site_list = entry;
765
766 return True;
767}
768
769/*
770 * Removes the window from the list of embedded drop sites for the toplevel.
771 * Returns True if the window was successfully removed, False otherwise.
772 */
773static Boolean
774remove_from_embedded_drop_site_list(Display* dpy, Window toplevel, Window window) {
775 EmbeddedDropSiteListEntry* entry = embedded_drop_site_list;
776 EmbeddedDropSiteListEntry* prev = NULL;
777
778 while (entry != NULL) {
779 if (entry->toplevel == toplevel) {
780 unsigned int idx;
781
782 for (idx = 0; idx < entry->embedded_sites_count; idx++) {
783 if (entry->embedded_sites[idx] == window) {
784 int tail = entry->embedded_sites_count - idx - 1;
785 if (tail > 0) {
786 memmove(entry->embedded_sites + idx,
787 entry->embedded_sites + idx + 1,
788 tail * sizeof(Window));
789 }
790 entry->embedded_sites_count--;
791
792 /* If the list of embedded drop sites for this toplevel
793 becomes empty - restore the original proxies and remove
794 the entry. */
795 if (entry->embedded_sites_count == 0) {
796 Widget w = XtWindowToWidget(dpy, toplevel);
797
798 if (w != NULL) {
799 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
800 Widget copy = w;
801 jobject peer = findPeer(&w);
802
803 if (!JNU_IsNull(env, peer) &&
804 (*env)->IsInstanceOf(env, peer,
805 get_MEmbedCanvasPeerClass(env)) == JNI_TRUE) {
806 remove_xembed_drop_target(env, peer);
807 }
808 } else {
809 EmbeddedDropSiteProtocolListEntry* xdnd_entry =
810 get_xdnd_protocol_entry_for_toplevel(toplevel);
811 EmbeddedDropSiteProtocolListEntry* motif_entry =
812 get_motif_protocol_entry_for_toplevel(toplevel);
813
814 if (xdnd_entry != NULL) {
815 if (xdnd_entry->overriden == True) {
816 XChangeProperty(dpy, toplevel, XA_XdndAware,
817 XA_ATOM, 32,
818 PropModeReplace,
819 (unsigned char*)&xdnd_entry->protocol_version,
820 1);
821
822 XChangeProperty(dpy, toplevel, XA_XdndProxy,
823 XA_WINDOW, 32,
824 PropModeReplace,
825 (unsigned char*)&xdnd_entry->proxy, 1);
826 } else {
827 XDeleteProperty(dpy, toplevel, XA_XdndAware);
828 XDeleteProperty(dpy, toplevel, XA_XdndProxy);
829 }
830 remove_xdnd_protocol_entry_for_toplevel(toplevel);
831 }
832
833 if (motif_entry != NULL) {
834 if (motif_entry->overriden == True) {
835 /* Request status */
836 int status;
837
838 Atom type;
839 int format;
840 unsigned long nitems;
841 unsigned long after;
842 unsigned char* data;
843
844 data = NULL;
845 status = XGetWindowProperty(dpy, toplevel,
846 _XA_MOTIF_DRAG_RECEIVER_INFO, 0, 0xFFFF,
847 False, AnyPropertyType, &type, &format,
848 &nitems, &after, &data);
849
850 if (status == Success && data != NULL && type != None &&
851 format == 8 && nitems >= MOTIF_RECEIVER_INFO_SIZE) {
852 unsigned char byte_order = read_card8((char*)data, 0);
853 void* p = (char*)data + 4;
854
855 DASSERT(MOTIF_BYTE_ORDER == byte_order);
856
857 if (MOTIF_BYTE_ORDER == byte_order) {
858 /* restore the original proxy window */
859 write_card32(&p, motif_entry->proxy);
860
861 XChangeProperty(dpy, toplevel,
862 _XA_MOTIF_DRAG_RECEIVER_INFO,
863 _XA_MOTIF_DRAG_RECEIVER_INFO, 8,
864 PropModeReplace,
865 (unsigned char*)data,
866 MOTIF_RECEIVER_INFO_SIZE);
867 }
868 }
869
870 if (status == Success) {
871 XFree(data);
872 }
873 } else {
874 XDeleteProperty(dpy, toplevel, _XA_MOTIF_DRAG_RECEIVER_INFO);
875 }
876
877 remove_motif_protocol_entry_for_toplevel(toplevel);
878 }
879
880 if ((entry->event_mask & PropertyChangeMask) == 0) {
881 XSelectInput(dpy, toplevel, entry->event_mask);
882 }
883 }
884
885 if (prev == NULL) {
886 embedded_drop_site_list = entry->next;
887 } else {
888 prev->next = entry->next;
889 }
890
891 free(entry);
892 }
893 return True;
894 }
895 }
896 return False;
897 }
898 prev = entry;
899 entry = entry->next;
900 }
901 return False;
902}
903
904static EmbeddedDropSiteListEntry*
905get_entry_for_toplevel(Window toplevel) {
906 EmbeddedDropSiteListEntry* entry = embedded_drop_site_list;
907
908 while (entry != NULL) {
909 if (entry->toplevel == toplevel) {
910 return entry;
911 }
912 entry = entry->next;
913 }
914 return NULL;
915}
916
917static EmbeddedDropSiteProtocolListEntry*
918get_motif_protocol_entry_for_toplevel(Window toplevel) {
919 EmbeddedDropSiteProtocolListEntry* entry = embedded_motif_protocol_list;
920
921 while (entry != NULL) {
922 if (entry->window == toplevel) {
923 return entry;
924 }
925 entry = entry->next;
926 }
927 return NULL;
928}
929
930static EmbeddedDropSiteProtocolListEntry*
931get_xdnd_protocol_entry_for_toplevel(Window toplevel) {
932 EmbeddedDropSiteProtocolListEntry* entry = embedded_xdnd_protocol_list;
933
934 while (entry != NULL) {
935 if (entry->window == toplevel) {
936 return entry;
937 }
938 entry = entry->next;
939 }
940 return NULL;
941}
942
943static void
944remove_motif_protocol_entry_for_toplevel(Window toplevel) {
945 EmbeddedDropSiteProtocolListEntry* entry = embedded_motif_protocol_list;
946 EmbeddedDropSiteProtocolListEntry* prev_entry = NULL;
947
948 while (entry != NULL) {
949 if (entry->window == toplevel) {
950 if (prev_entry != NULL) {
951 prev_entry->next = entry->next;
952 } else {
953 embedded_motif_protocol_list = entry->next;
954 }
955 free(entry);
956 }
957 entry = entry->next;
958 prev_entry = entry;
959 }
960}
961
962static void
963remove_xdnd_protocol_entry_for_toplevel(Window toplevel) {
964 EmbeddedDropSiteProtocolListEntry* entry = embedded_xdnd_protocol_list;
965 EmbeddedDropSiteProtocolListEntry* prev_entry = NULL;
966
967 while (entry != NULL) {
968 if (entry->window == toplevel) {
969 if (prev_entry != NULL) {
970 prev_entry->next = entry->next;
971 } else {
972 embedded_xdnd_protocol_list = entry->next;
973 }
974 free(entry);
975 }
976 entry = entry->next;
977 }
978}
979
980static Boolean
981is_embedding_toplevel(Window toplevel) {
982 return get_entry_for_toplevel(toplevel) != NULL;
983}
984
985static Window
986get_embedded_window(Display* dpy, Window toplevel, int x, int y) {
987 EmbeddedDropSiteListEntry* entry = get_entry_for_toplevel(toplevel);
988
989 if (entry != NULL) {
990 unsigned int idx;
991
992 for (idx = 0; idx < entry->embedded_sites_count; idx++) {
993 Window site = entry->embedded_sites[idx];
994 Window child = None;
995 int x_return, y_return;
996
997 if (XTranslateCoordinates(dpy, entry->root, site, x, y,
998 &x_return, &y_return, &child)) {
999 if (x_return >= 0 && y_return >= 0) {
1000 XWindowAttributes xwa;
1001 XGetWindowAttributes(dpy, site, &xwa);
1002 if (xwa.map_state != IsUnmapped &&
1003 x_return < xwa.width && y_return < xwa.height) {
1004 return site;
1005 }
1006 }
1007 }
1008 }
1009 }
1010
1011 return None;
1012}
1013
1014/*
1015 * If the toplevel is not an embedding toplevel does nothing and returns False.
1016 * Otherwise, sets xdnd_proxy for the specified toplevel to the 'proxy_window',
1017 * xdnd_protocol_version to 'version', xdnd_override to 'override', returns True.
1018 */
1019static Boolean
1020set_xdnd_proxy_for_toplevel(Window toplevel, Window proxy_window,
1021 unsigned int version, Boolean override) {
1022 EmbeddedDropSiteProtocolListEntry* entry =
1023 get_xdnd_protocol_entry_for_toplevel(toplevel);
1024
1025 if (entry == NULL) {
1026 return False;
1027 }
1028
1029 entry->proxy = proxy_window;
1030 entry->protocol_version = version;
1031 entry->overriden = override;
1032
1033 return True;
1034}
1035
1036/*
1037 * If the toplevel is not an embedding toplevel does nothing and returns False.
1038 * Otherwise, sets motif_proxy for the specified toplevel to the proxy_window,
1039 * motif_override to 'override' and returns True.
1040 */
1041static Boolean
1042set_motif_proxy_for_toplevel(Window toplevel, Window proxy_window, Boolean override) {
1043 EmbeddedDropSiteProtocolListEntry* entry =
1044 get_motif_protocol_entry_for_toplevel(toplevel);
1045
1046 if (entry == NULL) {
1047 return False;
1048 }
1049
1050 entry->proxy = proxy_window;
1051 entry->overriden = override;
1052
1053 return True;
1054}
1055
1056/*
1057 * Forwards a drag notification to the embedding toplevel modifying the event
1058 * to match the protocol version supported by the toplevel.
1059 * Returns True if the event is sent, False otherwise.
1060 */
1061static Boolean
1062forward_client_message_to_toplevel(Window toplevel, XClientMessageEvent* event) {
1063 EmbeddedDropSiteProtocolListEntry* protocol_entry = NULL;
1064 Window proxy = None;
1065
1066 if (event->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
1067 protocol_entry = get_motif_protocol_entry_for_toplevel(toplevel);
1068 } else {
1069 /* Assume XDnD */
1070 protocol_entry = get_xdnd_protocol_entry_for_toplevel(toplevel);
1071 if (protocol_entry != NULL) {
1072 /* Adjust the event to match the XDnD protocol version. */
1073 unsigned int version = protocol_entry->protocol_version;
1074 if (event->message_type == XA_XdndEnter) {
1075 unsigned int min_version = source_protocol_version < version ?
1076 source_protocol_version : version;
1077 event->data.l[1] = min_version << XDND_PROTOCOL_SHIFT;
1078 event->data.l[1] |= source_data_types_count > 3 ? XDND_DATA_TYPES_BIT : 0;
1079 }
1080 }
1081 }
1082
1083 if (protocol_entry == NULL) {
1084 return False;
1085 }
1086
1087 if (!protocol_entry->overriden) {
1088 return False;
1089 }
1090 proxy = protocol_entry->proxy;
1091
1092 if (proxy == None) {
1093 proxy = toplevel;
1094 }
1095
1096 event->window = toplevel;
1097
1098 XSendEvent(event->display, proxy, False, NoEventMask, (XEvent*)event);
1099
1100 return True;
1101}
1102
1103/******************************************************************************/
1104
1105/********************* Drop site list support *********************************/
1106
1107struct DropSiteListEntryRec;
1108
1109typedef struct DropSiteListEntryRec DropSiteListEntry;
1110
1111struct DropSiteListEntryRec {
1112 Window window;
1113 Window root;
1114 /*
1115 * The closest to the root ancestor with WM_STATE property set.
1116 * Normally toplevel == window.
1117 * In plugin scenario toplevel is the browser toplevel window.
1118 */
1119 Window toplevel;
1120 /*
1121 * Java top-level position is the outer canvas position, not the shell
1122 * window position. We need to keep the outer canvas ID (and the root ID) to
1123 * translate from mouse position root coordinates to the Java component
1124 * coordinates.
1125 */
1126 Window outer_canvas;
1127 jobject component;
1128 DropSiteListEntry* next;
1129};
1130
1131static DropSiteListEntry* drop_site_list = NULL;
1132
1133/*
1134 * If drop_site_list already contains an entry with the same window,
1135 * does nothing and returns False.
1136 * Otherwise, adds a new entry the list and returns True
1137 * if completes successfully.
1138 */
1139static Boolean
1140add_to_drop_site_list(Window window, Window root, Window toplevel,
1141 Window outer_canvas, jobject component) {
1142 DropSiteListEntry* entry = drop_site_list;
1143
1144 while (entry != NULL) {
1145 if (entry->window == window) {
1146 return False;
1147 }
1148 entry = entry->next;
1149 }
1150
1151 entry = malloc(sizeof(DropSiteListEntry));
1152
1153 if (entry == NULL) {
1154 return False;
1155 }
1156
1157 entry->window = window;
1158 entry->root = root;
1159 entry->toplevel = toplevel;
1160 entry->outer_canvas = outer_canvas;
1161 entry->component = component;
1162 entry->next = drop_site_list;
1163 drop_site_list = entry;
1164
1165 return True;
1166}
1167
1168/*
1169 * Returns True if the list entry for the specified window has been successfully
1170 * removed from the list. Otherwise, returns False.
1171 */
1172static Boolean
1173remove_from_drop_site_list(Window window) {
1174 DropSiteListEntry* entry = drop_site_list;
1175 DropSiteListEntry* prev = NULL;
1176
1177 while (entry != NULL) {
1178 if (entry->window == window) {
1179 if (prev != NULL) {
1180 prev->next = entry->next;
1181 } else {
1182 drop_site_list = entry->next;
1183 }
1184 free(entry);
1185 return True;
1186 }
1187 prev = entry;
1188 entry = entry->next;
1189 }
1190
1191 return False;
1192}
1193
1194static jobject
1195get_component_for_window(Window window) {
1196 DropSiteListEntry* entry = drop_site_list;
1197
1198 while (entry != NULL) {
1199 if (entry->window == window) {
1200 return entry->component;
1201 }
1202 entry = entry->next;
1203 }
1204
1205 return NULL;
1206}
1207
1208static Window
1209get_root_for_window(Window window) {
1210 DropSiteListEntry* entry = drop_site_list;
1211
1212 while (entry != NULL) {
1213 if (entry->window == window) {
1214 return entry->root;
1215 }
1216 entry = entry->next;
1217 }
1218
1219 return None;
1220}
1221
1222static Window
1223get_toplevel_for_window(Window window) {
1224 DropSiteListEntry* entry = drop_site_list;
1225
1226 while (entry != NULL) {
1227 if (entry->window == window) {
1228 return entry->toplevel;
1229 }
1230 entry = entry->next;
1231 }
1232
1233 return None;
1234}
1235
1236static Window
1237get_outer_canvas_for_window(Window window) {
1238 DropSiteListEntry* entry = drop_site_list;
1239
1240 while (entry != NULL) {
1241 if (entry->window == window) {
1242 return entry->outer_canvas;
1243 }
1244 entry = entry->next;
1245 }
1246
1247 return None;
1248}
1249/******************************************************************************/
1250
1251/******************* Delayed drop site registration stuff *********************/
1252struct DelayedRegistrationEntryRec;
1253
1254typedef struct DelayedRegistrationEntryRec DelayedRegistrationEntry;
1255
1256struct DelayedRegistrationEntryRec {
1257 Widget outer_canvas;
1258 jobject component;
1259 XtIntervalId timer;
1260 DelayedRegistrationEntry* next;
1261};
1262
1263static DelayedRegistrationEntry* delayed_registration_list = NULL;
1264
1265static const int DELAYED_REGISTRATION_PERIOD = 500;
1266
1267/* Timer callback. */
1268static void
1269register_drop_site_later(XtPointer client_data, XtIntervalId* id);
1270
1271/*
1272 * Enqueues the specified widget and component for delayed drop site
1273 * registration. If this widget has already been registered, does nothing and
1274 * returns False. Otherwise, schedules a timer callback that will repeatedly
1275 * attempt to register the drop site until the registration succeeds.
1276 * To remove this widget from the queue of delayed registration call
1277 * remove_delayed_registration_entry().
1278 *
1279 * The caller must own AWT_LOCK.
1280 */
1281static Boolean
1282add_delayed_registration_entry(Widget outer_canvas, XtPointer componentRef) {
1283 DelayedRegistrationEntry* entry = delayed_registration_list;
1284
1285 if (outer_canvas == NULL || componentRef == NULL) {
1286 return False;
1287 }
1288
1289 while (entry != NULL && entry->outer_canvas != outer_canvas) {
1290 entry = entry->next;
1291 }
1292
1293 if (entry != NULL) {
1294 return False;
1295 }
1296
1297 entry = malloc(sizeof(DelayedRegistrationEntry));
1298
1299 if (entry == NULL) {
1300 return False;
1301 }
1302
1303 entry->outer_canvas = outer_canvas;
1304 entry->component = componentRef;
1305 entry->timer = XtAppAddTimeOut(awt_appContext, DELAYED_REGISTRATION_PERIOD,
1306 register_drop_site_later, entry);
1307 entry->next = delayed_registration_list;
1308 delayed_registration_list = entry;
1309
1310 return True;
1311}
1312
1313/*
1314 * Unregisters the timer callback and removes this widget from the queue of
1315 * delayed drop site registration.
1316 *
1317 * The caller must own AWT_LOCK.
1318 */
1319static Boolean
1320remove_delayed_registration_entry(Widget outer_canvas) {
1321 DelayedRegistrationEntry* entry = delayed_registration_list;
1322 DelayedRegistrationEntry* prev = NULL;
1323
1324 if (outer_canvas == NULL) {
1325 return False;
1326 }
1327
1328 while (entry != NULL && entry->outer_canvas != outer_canvas) {
1329 prev = entry;
1330 entry = entry->next;
1331 }
1332
1333 if (entry == NULL) {
1334 return False;
1335 }
1336
1337 if (prev != NULL) {
1338 prev->next = entry->next;
1339 } else {
1340 delayed_registration_list = entry->next;
1341 }
1342
1343 if (entry->timer) {
1344 XtRemoveTimeOut(entry->timer);
1345 entry->timer = (XtIntervalId)0;
1346 }
1347
1348 free(entry);
1349
1350 return True;
1351}
1352
1353static void
1354register_drop_site_later(XtPointer client_data, XtIntervalId* id) {
1355 DelayedRegistrationEntry* entry = (DelayedRegistrationEntry*)client_data;
1356
1357 if (XtIsRealized(entry->outer_canvas) &&
1358 register_drop_site(entry->outer_canvas, entry->component)) {
1359 remove_delayed_registration_entry(entry->outer_canvas);
1360 } else {
1361 entry->timer = XtAppAddTimeOut(awt_appContext, DELAYED_REGISTRATION_PERIOD,
1362 register_drop_site_later, entry);
1363 }
1364}
1365/******************************************************************************/
1366
1367static void
1368awt_dnd_cleanup() {
1369 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1370
1371 if (!JNU_IsNull(env, target_component)) {
1372 /* Trigger dragExit */
1373 /*
1374 * Note: we pass NULL native context. This indicates that response
1375 * shouldn't be sent to the source.
1376 */
1377 dt_postDropTargetEvent(env, target_component, 0, 0,
1378 java_awt_dnd_DnDConstants_ACTION_NONE,
1379 java_awt_event_MouseEvent_MOUSE_EXITED,
1380 NULL);
1381 }
1382
1383 if (motif_top_level_leave_postponed) {
1384 XClientMessageEvent* leave = &motif_top_level_leave_postponed_event;
1385 if (leave->type == ClientMessage) {
1386 Window win = leave->window;
1387 if (is_embedding_toplevel(win)) {
1388 forward_client_message_to_toplevel(win, leave);
1389 }
1390 }
1391 }
1392
1393 if (source_window != None) {
1394 XSelectInput(awt_display, source_window, source_window_mask);
1395 }
1396
1397 source_protocol = NO_PROTOCOL;
1398 source_protocol_version = 0;
1399 source_window = None;
1400 source_atom = None;
1401 source_window_mask = 0;
1402 source_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
1403 track_source_actions = False;
1404 (*env)->DeleteGlobalRef(env, source_data_types);
1405 source_data_types = NULL;
1406 if (source_data_types_native != NULL) {
1407 free(source_data_types_native);
1408 source_data_types_native = NULL;
1409 }
1410 source_data_types_count = 0;
1411 source_x = 0;
1412 source_y = 0;
1413 target_component = NULL;
1414 motif_top_level_leave_postponed = False;
1415 memset(&motif_top_level_leave_postponed_event, 0,
1416 sizeof(XClientMessageEvent));
1417}
1418
1419static jlongArray
1420get_data_types_array(JNIEnv* env, Atom* types, unsigned int types_count) {
1421 jlongArray array = NULL;
1422 jboolean isCopy;
1423 jlong* jTargets;
1424#ifndef _LP64 /* Atom and jlong are different sizes in the 32-bit build */
1425 unsigned int i;
1426#endif
1427
1428 if ((*env)->PushLocalFrame(env, 1) < 0) {
1429 return NULL;
1430 }
1431
1432 array = (*env)->NewLongArray(env, types_count);
1433
1434 if (JNU_IsNull(env, array)) {
1435 return NULL;
1436 }
1437
1438 if (types_count == 0) {
1439 return array;
1440 }
1441
1442 jTargets = (*env)->GetLongArrayElements(env, array, &isCopy);
1443 if (jTargets == NULL) {
1444 (*env)->PopLocalFrame(env, NULL);
1445 return NULL;
1446 }
1447
1448#ifdef _LP64
1449 memcpy(jTargets, types, types_count * sizeof(Atom));
1450#else
1451 for (i = 0; i < types_count; i++) {
1452 jTargets[i] = (types[i] & 0xFFFFFFFFLU);
1453 }
1454#endif
1455
1456 (*env)->ReleaseLongArrayElements(env, array, jTargets, 0);
1457
1458 array = (*env)->NewGlobalRef(env, array);
1459
1460 (*env)->PopLocalFrame(env, NULL);
1461
1462 return array;
1463}
1464
1465static Boolean
1466is_xdnd_drag_message_type(unsigned long message_type) {
1467 return message_type == XA_XdndEnter ||
1468 message_type == XA_XdndPosition ||
1469 message_type == XA_XdndLeave ||
1470 message_type == XA_XdndDrop ? True : False;
1471}
1472
1473/*
1474 * Returns EventConsume if the event should be consumed,
1475 * EventPassAlong otherwise.
1476 */
1477static EventStatus
1478handle_xdnd_enter(XClientMessageEvent* event) {
1479 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1480 Display* dpy = event->display;
1481 long* event_data = event->data.l;
1482 Window source_win = None;
1483 long source_win_mask = 0;
1484 unsigned int protocol_version = 0;
1485 unsigned int data_types_count = 0;
1486 Atom* data_types = NULL;
1487 jlongArray java_data_types = NULL;
1488 jint actions = java_awt_dnd_DnDConstants_ACTION_NONE;
1489 Boolean track = False;
1490
1491 DTRACE_PRINTLN5("%s:%d XdndEnter comp=%X src_win=%ld protocol=%d.",
1492 __FILE__, __LINE__,
1493 target_component, source_window, source_protocol);
1494
1495 if (!JNU_IsNull(env, target_component) || source_window != None ||
1496 source_protocol != NO_PROTOCOL) {
1497 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid state.",
1498 __FILE__, __LINE__);
1499 return EventFailure;
1500 }
1501
1502 /*
1503 * NOTE: the component can be NULL if the event was sent to the embedding
1504 * toplevel.
1505 */
1506 if (JNU_IsNull(env, get_component_for_window(event->window)) &&
1507 !is_embedding_toplevel(event->window)) {
1508 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - window is not a registered drop site.",
1509 __FILE__, __LINE__);
1510 return EventFailure;
1511 }
1512
1513 protocol_version =
1514 (event_data[1] & XDND_PROTOCOL_MASK) >> XDND_PROTOCOL_SHIFT;
1515
1516 /* XDnD compliance only requires supporting version 3 and up. */
1517 if (protocol_version < XDND_MIN_PROTOCOL_VERSION) {
1518 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid protocol version.",
1519 __FILE__, __LINE__);
1520 return EventFailure;
1521 }
1522
1523 /* Ignore the source if the protocol version is higher than we support. */
1524 if (protocol_version > XDND_PROTOCOL_VERSION) {
1525 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid protocol version.",
1526 __FILE__, __LINE__);
1527 return EventFailure;
1528 }
1529
1530 source_win = event_data[0];
1531
1532 /* Extract the list of supported actions. */
1533 if (protocol_version < 2) {
1534 /* Prior to XDnD version 2 only COPY action was supported. */
1535 actions = java_awt_dnd_DnDConstants_ACTION_COPY;
1536 } else {
1537 unsigned char ret;
1538 Atom type;
1539 int format;
1540 unsigned long nitems;
1541 unsigned long after;
1542 unsigned char *data;
1543
1544 data = NULL;
1545 ret = checked_XGetWindowProperty(dpy, source_win, XA_XdndActionList,
1546 0, 0xFFFF, False, XA_ATOM, &type,
1547 &format, &nitems, &after, &data);
1548
1549 /* Ignore the source if the window is destroyed. */
1550 if (ret == BadWindow) {
1551 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.",
1552 __FILE__, __LINE__);
1553 return EventFailure;
1554 }
1555
1556 if (ret == Success) {
1557 if (type == XA_ATOM && format == 32) {
1558 unsigned int i;
1559 Atom* action_atoms = (Atom*)data;
1560
1561 for (i = 0; i < nitems; i++) {
1562 actions |= xdnd_to_java_action(action_atoms[i]);
1563 }
1564 }
1565
1566 /*
1567 * According to XDnD protocol, XdndActionList is optional.
1568 * If XdndActionList is not set we try to guess which actions are
1569 * supported.
1570 */
1571 if (type == None) {
1572 actions = java_awt_dnd_DnDConstants_ACTION_COPY;
1573 track = True;
1574 }
1575
1576 XFree(data);
1577 }
1578 }
1579
1580 /* Extract the available data types. */
1581 if (event_data[1] & XDND_DATA_TYPES_BIT) {
1582 unsigned char ret;
1583 Atom type;
1584 int format;
1585 unsigned long nitems;
1586 unsigned long after;
1587 unsigned char *data;
1588
1589 data = NULL;
1590 ret = checked_XGetWindowProperty(dpy, source_win, XA_XdndTypeList,
1591 0, 0xFFFF, False, XA_ATOM, &type,
1592 &format, &nitems, &after, &data);
1593
1594 /* Ignore the source if the window is destroyed. */
1595 if (ret == BadWindow) {
1596 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.",
1597 __FILE__, __LINE__);
1598 return EventFailure;
1599 }
1600
1601 if (ret == Success) {
1602 if (type == XA_ATOM && format == 32 && nitems > 0) {
1603 data_types_count = nitems;
1604 data_types = (Atom*)malloc(data_types_count * sizeof(Atom));
1605
1606 if (data_types == NULL) {
1607 XFree(data);
1608 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - malloc fails.",
1609 __FILE__, __LINE__);
1610 return EventFailure;
1611 }
1612
1613 memcpy((void *)data_types, (void *)data,
1614 data_types_count * sizeof(Atom));
1615 }
1616
1617 XFree(data);
1618 }
1619 } else {
1620 int i;
1621 data_types = (Atom*)malloc(3 * sizeof (Atom));
1622 if (data_types == NULL) {
1623 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - malloc fails.",
1624 __FILE__, __LINE__);
1625 return EventFailure;
1626 }
1627 for (i = 0; i < 3; i++) {
1628 Atom j;
1629 if ((j = event_data[2 + i]) != None) {
1630 data_types[data_types_count++] = j;
1631 }
1632 }
1633 }
1634
1635 java_data_types = get_data_types_array(env, data_types, data_types_count);
1636
1637 if (JNU_IsNull(env, java_data_types)) {
1638 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - cannot create types array.",
1639 __FILE__, __LINE__);
1640 free((char*)data_types);
1641 return EventFailure;
1642 }
1643
1644 /*
1645 * Select for StructureNotifyMask to receive DestroyNotify in case of source
1646 * crash.
1647 */
1648 {
1649 unsigned char ret;
1650 XWindowAttributes xwa;
1651
1652 XGetWindowAttributes(dpy, source_win, &xwa);
1653
1654 source_win_mask = xwa.your_event_mask;
1655
1656 ret = checked_XSelectInput(dpy, source_win,
1657 (source_win_mask | StructureNotifyMask));
1658
1659 if (ret == BadWindow) {
1660 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.",
1661 __FILE__, __LINE__);
1662 free((char*)data_types);
1663 (*env)->DeleteGlobalRef(env, java_data_types);
1664 return EventFailure;
1665 }
1666 }
1667
1668 /* Update the global state. */
1669 source_protocol = XDND_PROTOCOL;
1670 source_protocol_version = protocol_version;
1671 source_window = source_win;
1672 source_window_mask = source_win_mask;
1673 source_actions = actions;
1674 track_source_actions = track;
1675 source_data_types = java_data_types;
1676 source_data_types_native = data_types;
1677 source_data_types_count = data_types_count;
1678
1679 DTRACE_PRINTLN5("%s:%d XdndEnter handled src_win=%ld protocol=%d fmt=%d.",
1680 __FILE__, __LINE__,
1681 source_window, source_protocol, data_types_count);
1682
1683 return EventSuccess;
1684}
1685
1686/*
1687 * Returns EventConsume if the event should be consumed,
1688 * EventPassAlong otherwise.
1689 */
1690static EventStatus
1691handle_xdnd_position(XClientMessageEvent* event) {
1692 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1693 long* event_data = event->data.l;
1694 Window source_win = None;
1695 Time time_stamp = CurrentTime;
1696 Atom action_atom = None;
1697 jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
1698 int x = 0;
1699 int y = 0;
1700 jint java_event_id = 0;
1701 jobject component = NULL;
1702 Window receiver = None;
1703
1704 DTRACE_PRINTLN5("%s:%d XdndPosition comp=%X src_win=%ld protocol=%d.",
1705 __FILE__, __LINE__,
1706 target_component, source_window, source_protocol);
1707
1708 if (source_protocol != XDND_PROTOCOL) {
1709 DTRACE_PRINTLN2("%s:%d XdndPosition rejected - invalid state.",
1710 __FILE__, __LINE__);
1711 return EventFailure;
1712 }
1713
1714 source_win = event_data[0];
1715
1716 /* Ignore XDnD messages from all other windows. */
1717 if (source_window != source_win) {
1718 DTRACE_PRINTLN4("%s:%d XdndPosition rejected - invalid source window cur=%ld this=%ld.",
1719 __FILE__, __LINE__, source_window, source_win);
1720 return EventFailure;
1721 }
1722
1723 x = event_data[2] >> 16;
1724 y = event_data[2] & 0xFFFF;
1725
1726 component = get_component_for_window(event->window);
1727
1728 if (JNU_IsNull(env, component)) {
1729 /*
1730 * The window must be the embedding toplevel, since otherwise we would reject the
1731 * XdndEnter and never get to this point.
1732 */
1733 DASSERT(is_embedding_toplevel(event->window));
1734
1735 receiver = get_embedded_window(event->display, event->window, x, y);
1736
1737 if (receiver != None) {
1738 component = get_component_for_window(receiver);
1739 }
1740 } else {
1741 receiver = event->window;
1742 }
1743
1744 /* Translate mouse position from root coordinates
1745 to the target window coordinates. */
1746 if (receiver != None) {
1747 Window child = None;
1748 XTranslateCoordinates(event->display,
1749 get_root_for_window(receiver),
1750 get_outer_canvas_for_window(receiver),
1751 x, y, &x, &y, &child);
1752 }
1753
1754 /* Time stamp - new in XDnD version 1. */
1755 if (source_protocol_version > 0) {
1756 time_stamp = event_data[3];
1757 }
1758
1759 /* User action - new in XDnD version 1. */
1760 if (source_protocol_version > 1) {
1761 action_atom = event_data[4];
1762 } else {
1763 /* The default action is XdndActionCopy */
1764 action_atom = XA_XdndActionCopy;
1765 }
1766
1767 action = xdnd_to_java_action(action_atom);
1768
1769 if (track_source_actions) {
1770 source_actions |= action;
1771 }
1772
1773 if (JNU_IsNull(env, component)) {
1774 if (!JNU_IsNull(env, target_component)) {
1775 dt_postDropTargetEvent(env, target_component, x, y,
1776 java_awt_dnd_DnDConstants_ACTION_NONE,
1777 java_awt_event_MouseEvent_MOUSE_EXITED,
1778 NULL);
1779 }
1780 } else {
1781 if (JNU_IsNull(env, target_component)) {
1782 java_event_id = java_awt_event_MouseEvent_MOUSE_ENTERED;
1783 } else {
1784 java_event_id = java_awt_event_MouseEvent_MOUSE_DRAGGED;
1785 }
1786
1787 dt_postDropTargetEvent(env, component, x, y, action,
1788 java_event_id, event);
1789 }
1790
1791 user_action = action;
1792 source_x = x;
1793 source_y = y;
1794 target_component = component;
1795
1796 return EventSuccess;
1797}
1798
1799/*
1800 * Returns EventConsume if the event should be consumed,
1801 * EventPassAlong otherwise.
1802 */
1803static EventStatus
1804handle_xdnd_leave(XClientMessageEvent* event) {
1805 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1806 long* event_data = event->data.l;
1807 Window source_win = None;
1808
1809 if (source_protocol != XDND_PROTOCOL) {
1810 DTRACE_PRINTLN2("%s:%d XdndLeave rejected - invalid state.",
1811 __FILE__, __LINE__);
1812 return EventFailure;
1813 }
1814
1815 source_win = event_data[0];
1816
1817 /* Ignore XDnD messages from all other windows. */
1818 if (source_window != source_win) {
1819 DTRACE_PRINTLN4("%s:%d XdndLeave rejected - invalid source window cur=%ld this=%ld.",
1820 __FILE__, __LINE__, source_window, source_win);
1821 return EventFailure;
1822 }
1823
1824 awt_dnd_cleanup();
1825
1826 return EventSuccess;
1827}
1828
1829/*
1830 * Returns EventConsume if the event should be consumed,
1831 * EventPassAlong otherwise.
1832 */
1833static EventStatus
1834handle_xdnd_drop(XClientMessageEvent* event) {
1835 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1836 long* event_data = event->data.l;
1837 Window source_win = None;
1838
1839 DTRACE_PRINTLN5("%s:%d XdndDrop comp=%X src_win=%ld protocol=%d.",
1840 __FILE__, __LINE__,
1841 target_component, source_window, source_protocol);
1842
1843 if (source_protocol != XDND_PROTOCOL) {
1844 DTRACE_PRINTLN2("%s:%d XdndDrop rejected - invalid state.",
1845 __FILE__, __LINE__);
1846 return EventFailure;
1847 }
1848
1849 source_win = event_data[0];
1850
1851 /* Ignore XDnD messages from all other windows. */
1852 if (source_window != source_win) {
1853 DTRACE_PRINTLN4("%s:%d XdndDrop rejected - invalid source window cur=%ld this=%ld.",
1854 __FILE__, __LINE__, source_window, source_win);
1855 return EventFailure;
1856 }
1857
1858 if (!JNU_IsNull(env, target_component)) {
1859 dt_postDropTargetEvent(env, target_component, source_x, source_y, user_action,
1860 java_awt_event_MouseEvent_MOUSE_RELEASED, event);
1861 }
1862
1863 return EventSuccess;
1864}
1865
1866/*
1867 * Returns EventPassAlong if the event should be passed to the original proxy.
1868 * TOP_LEVEL_ENTER should be passed to the original proxy only if the event is
1869 * invalid.
1870 */
1871static EventStatus
1872handle_motif_top_level_enter(XClientMessageEvent* event) {
1873 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1874 Display* dpy = event->display;
1875 char* event_data = event->data.b;
1876 unsigned char event_byte_order = 0;
1877 Window source_win = None;
1878 long source_win_mask = 0;
1879 unsigned int protocol_version = MOTIF_DND_PROTOCOL_VERSION;
1880 Atom property_atom = None;
1881 unsigned int data_types_count = 0;
1882 Atom* data_types = NULL;
1883 jlongArray java_data_types = NULL;
1884
1885 DTRACE_PRINTLN5("%s:%d TOP_LEVEL_ENTER comp=%X src_win=%ld protocol=%d.",
1886 __FILE__, __LINE__,
1887 target_component, source_window, source_protocol);
1888
1889 if (!JNU_IsNull(env, target_component) || source_window != None ||
1890 source_protocol != NO_PROTOCOL) {
1891 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - invalid state.",
1892 __FILE__, __LINE__);
1893 return EventFailure;
1894 }
1895
1896 if (JNU_IsNull(env, get_component_for_window(event->window)) &&
1897 !is_embedding_toplevel(event->window)) {
1898 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - window is not a registered drop site.",
1899 __FILE__, __LINE__);
1900 return EventFailure;
1901 }
1902
1903 event_byte_order = read_card8(event_data, 1);
1904 source_win = read_card32(event_data, 8, event_byte_order);
1905 property_atom = read_card32(event_data, 12, event_byte_order);
1906
1907 /* Extract the available data types. */
1908 {
1909 unsigned char ret;
1910 Atom type;
1911 int format;
1912 unsigned long nitems;
1913 unsigned long after;
1914 unsigned char *data;
1915
1916 data = NULL;
1917 ret = checked_XGetWindowProperty(dpy, source_win, property_atom, 0,
1918 0xFFFF, False,
1919 _XA_MOTIF_DRAG_INITIATOR_INFO, &type,
1920 &format, &nitems, &after, &data);
1921
1922 /* Ignore the source if the window is destroyed. */
1923 if (ret == BadWindow) {
1924 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - invalid window.",
1925 __FILE__, __LINE__);
1926 return EventFailure;
1927 }
1928
1929 if (ret == BadAtom) {
1930 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - invalid property atom.",
1931 __FILE__, __LINE__);
1932 return EventFailure;
1933 }
1934
1935 if (ret == Success) {
1936 if (type == _XA_MOTIF_DRAG_INITIATOR_INFO && format == 8 &&
1937 nitems == MOTIF_INITIATOR_INFO_SIZE) {
1938 unsigned char property_byte_order = read_card8((char*)data, 0);
1939 int index = read_card16((char*)data, 2, property_byte_order);
1940
1941 protocol_version = read_card8((char*)data, 1);
1942
1943 if (protocol_version > MOTIF_DND_PROTOCOL_VERSION) {
1944 DTRACE_PRINTLN3("%s:%d TOP_LEVEL_ENTER rejected - invalid protocol version: %d.",
1945 __FILE__, __LINE__, protocol_version);
1946 XFree(data);
1947 return EventFailure;
1948 }
1949
1950 get_target_list_for_index(dpy, index, &data_types, &data_types_count);
1951 }
1952
1953 XFree(data);
1954 }
1955 }
1956
1957 java_data_types = get_data_types_array(env, data_types, data_types_count);
1958
1959 if (JNU_IsNull(env, java_data_types)) {
1960 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_ENTER rejected - cannot create types array.",
1961 __FILE__, __LINE__);
1962 free((char*)data_types);
1963 return EventFailure;
1964 }
1965
1966 /*
1967 * Select for StructureNotifyMask to receive DestroyNotify in case of source
1968 * crash.
1969 */
1970 {
1971 unsigned char ret;
1972 XWindowAttributes xwa;
1973
1974 XGetWindowAttributes(dpy, source_win, &xwa);
1975
1976 source_win_mask = xwa.your_event_mask;
1977
1978 ret = checked_XSelectInput(dpy, source_win,
1979 (source_win_mask | StructureNotifyMask));
1980
1981 if (ret == BadWindow) {
1982 DTRACE_PRINTLN2("%s:%d XdndEnter rejected - invalid window.",
1983 __FILE__, __LINE__);
1984 free((char*)data_types);
1985 (*env)->DeleteGlobalRef(env, java_data_types);
1986 return EventFailure;
1987 }
1988 }
1989
1990 source_protocol = MOTIF_DND_PROTOCOL;
1991 source_protocol_version = protocol_version;
1992 source_window = source_win;
1993 source_atom = property_atom;
1994 source_window_mask = source_win_mask;
1995 /*
1996 * TOP_LEVEL_ENTER doesn't communicate the list of supported actions
1997 * They are provided in DRAG_MOTION.
1998 */
1999 source_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
2000 track_source_actions = False;
2001 source_data_types = java_data_types;
2002 source_data_types_native = data_types;
2003 source_data_types_count = data_types_count;
2004 DTRACE_PRINTLN6("%s:%d TOP_LEVEL_ENTER comp=%d src_win=%ld protocol=%d fmt=%d.",
2005 __FILE__, __LINE__,
2006 target_component, source_window, source_protocol, data_types_count);
2007
2008 return EventSuccess;
2009}
2010
2011/*
2012 * Returns EventPassAlong if the event should be passed to the original proxy.
2013 * DRAG_MOTION event shouldn't be passed to the original proxy only if it is
2014 * a valid event and the mouse coordinates passed in it specify the point over
2015 * a Java component in this JVM.
2016 */
2017static EventStatus
2018handle_motif_drag_motion(XClientMessageEvent* event) {
2019 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
2020 char* event_data = event->data.b;
2021 unsigned char event_reason = 0;
2022 unsigned char event_byte_order = 0;
2023 Window source_win = None;
2024 CARD16 flags = 0;
2025 unsigned char motif_action = 0;
2026 unsigned char motif_actions = 0;
2027 jint java_action = java_awt_dnd_DnDConstants_ACTION_NONE;
2028 jint java_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
2029 int x = 0;
2030 int y = 0;
2031 jint java_event_id = 0;
2032 jobject component = NULL;
2033
2034 DTRACE_PRINTLN5("%s:%d DRAG_MOTION comp=%X src_win=%ld protocol=%d.",
2035 __FILE__, __LINE__,
2036 target_component, source_window, source_protocol);
2037
2038 if (source_protocol != MOTIF_DND_PROTOCOL) {
2039 DTRACE_PRINTLN2("%s:%d DRAG_MOTION rejected - invalid state.",
2040 __FILE__, __LINE__);
2041 return EventFailure;
2042 }
2043
2044 event_reason = read_card8(event_data, 0) & MOTIF_MESSAGE_REASON_MASK;
2045 event_byte_order = read_card8(event_data, 1);
2046
2047 flags = read_card16(event_data, 2, event_byte_order);
2048
2049 motif_action = (flags & MOTIF_DND_ACTION_MASK) >> MOTIF_DND_ACTION_SHIFT;
2050 motif_actions = (flags & MOTIF_DND_ACTIONS_MASK) >> MOTIF_DND_ACTIONS_SHIFT;
2051
2052 java_action = motif_to_java_actions(motif_action);
2053 java_actions = motif_to_java_actions(motif_actions);
2054
2055 /* Append source window id to the event data, so that we can send the
2056 response properly. */
2057 {
2058 Window win = source_window;
2059 void* p = &event->data.b[12];
2060 if (event_byte_order != MOTIF_BYTE_ORDER) {
2061 SWAP4BYTES(win);
2062 }
2063 write_card32(&p, (CARD32)win);
2064 }
2065
2066 component = get_component_for_window(event->window);
2067
2068 if (event_reason == OPERATION_CHANGED) {
2069 /* OPERATION_CHANGED event doesn't provide coordinates, so we use
2070 previously stored position and component ref. */
2071 x = source_x;
2072 y = source_y;
2073
2074 if (JNU_IsNull(env, component)) {
2075 component = target_component;
2076 }
2077 } else {
2078 Window receiver = None;
2079
2080 x = read_card16(event_data, 8, event_byte_order);
2081 y = read_card16(event_data, 10, event_byte_order);
2082
2083 if (JNU_IsNull(env, component)) {
2084 /*
2085 * The window must be the embedding toplevel, since otherwise we
2086 * would reject the TOP_LEVEL_ENTER and never get to this point.
2087 */
2088 DASSERT(is_embedding_toplevel(event->window));
2089
2090 receiver = get_embedded_window(event->display, event->window, x, y);
2091
2092 if (receiver != None) {
2093 component = get_component_for_window(receiver);
2094 }
2095 } else {
2096 receiver = event->window;
2097 }
2098
2099 /* Translate mouse position from root coordinates
2100 to the target window coordinates. */
2101 if (receiver != None) {
2102 Window child = None;
2103 XTranslateCoordinates(event->display,
2104 get_root_for_window(receiver),
2105 get_outer_canvas_for_window(receiver),
2106 x, y, &x, &y, &child);
2107 }
2108 }
2109
2110 if (JNU_IsNull(env, component)) {
2111 if (!JNU_IsNull(env, target_component)) {
2112 /* Triggers dragExit */
2113 dt_postDropTargetEvent(env, target_component, x, y,
2114 java_awt_dnd_DnDConstants_ACTION_NONE,
2115 java_awt_event_MouseEvent_MOUSE_EXITED,
2116 NULL);
2117 }
2118 } else {
2119 if (JNU_IsNull(env, target_component)) {
2120 /* Triggers dragEnter */
2121 java_event_id = java_awt_event_MouseEvent_MOUSE_ENTERED;
2122 } else {
2123 /* Triggers dragOver */
2124 java_event_id = java_awt_event_MouseEvent_MOUSE_DRAGGED;
2125 }
2126
2127 dt_postDropTargetEvent(env, component, x, y, java_action, java_event_id,
2128 event);
2129 }
2130
2131 source_actions = java_actions;
2132 track_source_actions = False;
2133 user_action = java_action;
2134 source_x = x;
2135 source_y = y;
2136 target_component = component;
2137
2138 return EventSuccess;
2139}
2140
2141/*
2142 * Returns EventPassAlong if the event should be passed to the original proxy.
2143 * TOP_LEVEL_LEAVE should be passed to the original proxy only if the event
2144 * is invalid.
2145 */
2146static EventStatus
2147handle_motif_top_level_leave(XClientMessageEvent* event) {
2148 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
2149 char* event_data = event->data.b;
2150 unsigned char event_byte_order = 0;
2151 Window source_win = None;
2152
2153 DTRACE_PRINTLN5("%s:%d TOP_LEVEL_LEAVE comp=%X src_win=%ld protocol=%d.",
2154 __FILE__, __LINE__,
2155 target_component, source_window, source_protocol);
2156
2157 if (source_protocol != MOTIF_DND_PROTOCOL) {
2158 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_LEAVE rejected - invalid state.",
2159 __FILE__, __LINE__);
2160 return EventFailure;
2161 }
2162
2163 event_byte_order = read_card8(event_data, 1);
2164 source_win = read_card32(event_data, 8, event_byte_order);
2165
2166 /* Ignore Motif DnD messages from all other windows. */
2167 if (source_window != source_win) {
2168 DTRACE_PRINTLN4("%s:%d TOP_LEVEL_LEAVE rejected - invalid source window cur=%ld this=%ld.",
2169 __FILE__, __LINE__, source_window, source_win);
2170 return EventFailure;
2171 }
2172
2173 /*
2174 * Postpone upcall to java, so that we can abort it in case
2175 * if drop immediatelly follows (see BugTraq ID 4395290).
2176 * Send a dummy ClientMessage event to guarantee that a postponed java
2177 * upcall will be processed.
2178 */
2179 motif_top_level_leave_postponed = True;
2180 {
2181 XClientMessageEvent dummy;
2182 Window proxy;
2183
2184 dummy.display = event->display;
2185 dummy.type = ClientMessage;
2186 dummy.window = event->window;
2187 dummy.format = 32;
2188 dummy.message_type = None;
2189
2190 /*
2191 * If this is an embedded drop site, the event should go to the
2192 * awt_root_window as this is a proxy for all embedded drop sites.
2193 * Otherwise the event should go to the event->window, as we don't use
2194 * proxies for normal drop sites.
2195 */
2196 if (is_embedding_toplevel(event->window)) {
2197 proxy = get_awt_root_window();
2198 } else {
2199 proxy = event->window;
2200 }
2201
2202 XSendEvent(event->display, proxy, False, NoEventMask,
2203 (XEvent*)&dummy);
2204 }
2205
2206 return EventSuccess;
2207}
2208
2209/*
2210 * Returns EventPassAlong if the event should be passed to the original proxy.
2211 * DROP_START event shouldn't be passed to the original proxy only if it is
2212 * a valid event and the mouse coordinates passed in it specify the point over
2213 * a Java component in this JVM.
2214 */
2215static EventStatus
2216handle_motif_drop_start(XClientMessageEvent* event) {
2217 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
2218 char* event_data = event->data.b;
2219 unsigned char event_byte_order = 0;
2220 Window source_win = None;
2221 Atom property_atom = None;
2222 CARD16 flags = 0;
2223 unsigned char motif_action = 0;
2224 unsigned char motif_actions = 0;
2225 jint java_action = java_awt_dnd_DnDConstants_ACTION_NONE;
2226 jint java_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
2227 int x = 0;
2228 int y = 0;
2229 jobject component = NULL;
2230 Window receiver = None;
2231
2232 DTRACE_PRINTLN5("%s:%d DROP_START comp=%X src_win=%ld protocol=%d.",
2233 __FILE__, __LINE__,
2234 target_component, source_window, source_protocol);
2235
2236 if (source_protocol != MOTIF_DND_PROTOCOL) {
2237 DTRACE_PRINTLN2("%s:%d DROP_START rejected - invalid state.",
2238 __FILE__, __LINE__);
2239 return EventFailure;
2240 }
2241
2242 event_byte_order = read_card8(event_data, 1);
2243 source_win = read_card32(event_data, 16, event_byte_order);
2244
2245 /* Ignore Motif DnD messages from all other windows. */
2246 if (source_window != source_win) {
2247 DTRACE_PRINTLN4("%s:%d DROP_START rejected - invalid source window cur=%ld this=%ld.",
2248 __FILE__, __LINE__, source_window, source_win);
2249 return EventFailure;
2250 }
2251
2252 property_atom = read_card32(event_data, 12, event_byte_order);
2253
2254 flags = read_card16(event_data, 2, event_byte_order);
2255
2256 motif_action = (flags & MOTIF_DND_ACTION_MASK) >> MOTIF_DND_ACTION_SHIFT;
2257 motif_actions = (flags & MOTIF_DND_ACTIONS_MASK) >> MOTIF_DND_ACTIONS_SHIFT;
2258
2259 java_action = motif_to_java_actions(motif_action);
2260 java_actions = motif_to_java_actions(motif_actions);
2261
2262 x = read_card16(event_data, 8, event_byte_order);
2263 y = read_card16(event_data, 10, event_byte_order);
2264
2265 source_actions = java_actions;
2266
2267 component = get_component_for_window(event->window);
2268
2269 if (JNU_IsNull(env, component)) {
2270 /*
2271 * The window must be the embedding toplevel, since otherwise we would reject the
2272 * TOP_LEVEL_ENTER and never get to this point.
2273 */
2274 DASSERT(is_embedding_toplevel(event->window));
2275
2276 receiver = get_embedded_window(event->display, event->window, x, y);
2277
2278 if (receiver != None) {
2279 component = get_component_for_window(receiver);
2280 }
2281 } else {
2282 receiver = event->window;
2283 }
2284
2285 /* Translate mouse position from root coordinates
2286 to the target window coordinates. */
2287 if (receiver != None) {
2288 Window child = None;
2289 XTranslateCoordinates(event->display,
2290 get_root_for_window(receiver),
2291 get_outer_canvas_for_window(receiver),
2292 x, y, &x, &y, &child);
2293 }
2294
2295 if (JNU_IsNull(env, component)) {
2296 if (!JNU_IsNull(env, target_component)) {
2297 /* Triggers dragExit */
2298 dt_postDropTargetEvent(env, target_component, x, y,
2299 java_awt_dnd_DnDConstants_ACTION_NONE,
2300 java_awt_event_MouseEvent_MOUSE_EXITED,
2301 NULL);
2302 }
2303 } else {
2304 dt_postDropTargetEvent(env, component, x, y, java_action,
2305 java_awt_event_MouseEvent_MOUSE_RELEASED,
2306 event);
2307 }
2308
2309 return EventSuccess;
2310}
2311
2312static void
2313send_enter_message_to_toplevel(Window toplevel, XClientMessageEvent* xclient) {
2314 XClientMessageEvent enter;
2315
2316 if (source_protocol == XDND_PROTOCOL) {
2317 enter.display = xclient->display;
2318 enter.type = ClientMessage;
2319 enter.window = toplevel;
2320 enter.format = 32;
2321 enter.message_type = XA_XdndEnter;
2322 enter.data.l[0] = xclient->data.l[0]; /* XID of the source window */
2323 enter.data.l[1] = source_protocol_version << XDND_PROTOCOL_SHIFT;
2324 enter.data.l[1] |= source_data_types_count > 3 ? XDND_DATA_TYPES_BIT : 0;
2325 enter.data.l[2] =
2326 source_data_types_count > 0 ? source_data_types_native[0] : None;
2327 enter.data.l[3] =
2328 source_data_types_count > 1 ? source_data_types_native[1] : None;
2329 enter.data.l[4] =
2330 source_data_types_count > 2 ? source_data_types_native[2] : None;
2331 } else if (source_protocol == MOTIF_DND_PROTOCOL) {
2332 int reason = (int)(xclient->data.b[0] & MOTIF_MESSAGE_REASON_MASK);
2333 unsigned char byte_order = xclient->data.b[1];
2334
2335 enter.display = xclient->display;
2336 enter.type = ClientMessage;
2337 enter.window = toplevel;
2338 enter.format = 8;
2339 enter.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
2340
2341 {
2342 void* p = &enter.data.b[0];
2343 int flags = 0;
2344
2345 flags |= java_to_motif_actions(user_action) << MOTIF_DND_ACTION_SHIFT;
2346 flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
2347
2348 write_card8(&p, TOP_LEVEL_ENTER | MOTIF_MESSAGE_FROM_INITIATOR);
2349 write_card8(&p, byte_order);
2350 write_card16(&p, flags);
2351 {
2352 Time time_stamp = read_card32(xclient->data.b, 4, byte_order);
2353 Window src_window = source_window;
2354 Atom motif_atom = _XA_MOTIF_ATOM_0;
2355
2356 if (byte_order != MOTIF_BYTE_ORDER) {
2357 SWAP4BYTES(time_stamp);
2358 SWAP4BYTES(src_window);
2359 SWAP4BYTES(motif_atom);
2360 }
2361 write_card32(&p, time_stamp);
2362 write_card32(&p, src_window);
2363 write_card32(&p, motif_atom);
2364 }
2365 }
2366 } else {
2367 return;
2368 }
2369
2370 forward_client_message_to_toplevel(toplevel, &enter);
2371}
2372
2373static void
2374send_leave_message_to_toplevel(Window toplevel, XClientMessageEvent* xclient) {
2375 XClientMessageEvent leave;
2376
2377 if (source_protocol == XDND_PROTOCOL) {
2378 leave.display = xclient->display;
2379 leave.type = ClientMessage;
2380 leave.window = toplevel;
2381 leave.format = 32;
2382 leave.message_type = XA_XdndLeave;
2383 leave.data.l[0] = xclient->data.l[0]; /* XID of the source window */
2384 leave.data.l[1] = 0; /* flags */
2385 } else if (source_protocol == MOTIF_DND_PROTOCOL) {
2386 int reason = (int)(xclient->data.b[0] & MOTIF_MESSAGE_REASON_MASK);
2387 unsigned char byte_order = xclient->data.b[1];
2388
2389 leave.display = xclient->display;
2390 leave.type = ClientMessage;
2391 leave.window = toplevel;
2392 leave.format = 8;
2393 leave.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
2394
2395 {
2396 void* p = &leave.data.b[0];
2397 int flags = 0;
2398
2399 write_card8(&p, TOP_LEVEL_LEAVE | MOTIF_MESSAGE_FROM_INITIATOR);
2400 write_card8(&p, byte_order);
2401
2402 {
2403 Time time_stamp = read_card32(xclient->data.b, 4, byte_order);
2404 Window src_window = source_window;
2405
2406 if (byte_order != MOTIF_BYTE_ORDER) {
2407 SWAP4BYTES(time_stamp);
2408 SWAP4BYTES(src_window);
2409 }
2410 write_card32(&p, time_stamp);
2411 write_card32(&p, src_window);
2412 }
2413 }
2414 } else {
2415 return;
2416 }
2417
2418 forward_client_message_to_toplevel(toplevel, &leave);
2419}
2420
2421static void
2422post_process_client_message(XClientMessageEvent* xclient, EventStatus status,
2423 EventType type) {
2424 Window win = xclient->window;
2425 Boolean postponed_leave = motif_top_level_leave_postponed;
2426
2427 motif_top_level_leave_postponed = False;
2428
2429 if (is_embedding_toplevel(win)) {
2430 Boolean server_grabbed = False;
2431
2432 if (postponed_leave) {
2433 XClientMessageEvent* leave = &motif_top_level_leave_postponed_event;
2434 DASSERT(leave->type == ClientMessage && type == DropEvent);
2435 /* Grab the server to ensure that no event is sent between
2436 the TOP_LEVEL_LEAVE and the next message. */
2437 XGrabServer(awt_display);
2438 forward_client_message_to_toplevel(leave->window, leave);
2439 memset(&motif_top_level_leave_postponed_event, 0,
2440 sizeof(XClientMessageEvent));
2441 }
2442
2443 /*
2444 * This code forwards drag notifications to the browser according to the
2445 * following rules:
2446 * - the messages that we failed to process are always forwarded to the
2447 * browser;
2448 * - MotionEvents and DropEvents are forwarded if and only if the drag
2449 * is not over a plugin window;
2450 * - XDnD: EnterEvents and LeaveEvents are never forwarded, instead, we
2451 * send synthesized EnterEvents or LeaveEvents when the drag
2452 * respectively exits or enters plugin windows;
2453 * - Motif DnD: EnterEvents and LeaveEvents are always forwarded.
2454 * Synthetic EnterEvents and LeaveEvents are needed, because the XDnD drop
2455 * site implemented Netscape 6.2 has a nice feature: when it receives
2456 * the first XdndPosition it continuously sends XdndStatus messages to
2457 * the source (every 100ms) until the drag terminates or leaves the drop
2458 * site. When the mouse is dragged over plugin window embedded in the
2459 * browser frame, these XdndStatus messages are mixed with the XdndStatus
2460 * messages sent from the plugin.
2461 * For Motif DnD, synthetic events cause Motif warnings being displayed,
2462 * so these events are always forwarded. However, Motif DnD drop site in
2463 * Netscape 6.2 is implemented in the same way, so there could be similar
2464 * problems if the drag source choose Motif DnD for communication.
2465 */
2466 switch (status) {
2467 case EventFailure:
2468 forward_client_message_to_toplevel(win, xclient);
2469 break;
2470 case EventSuccess:
2471 {
2472 /* True iff the previous notification was MotionEvent and it was
2473 forwarded to the browser. */
2474 static Boolean motion_passed_along = False;
2475
2476 Boolean motif_protocol =
2477 xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
2478
2479 switch (type) {
2480 case MotionEvent:
2481 if (JNU_IsNull(env, target_component)) {
2482 if (!motion_passed_along && !motif_protocol) {
2483 send_enter_message_to_toplevel(win, xclient);
2484 }
2485 forward_client_message_to_toplevel(win, xclient);
2486 motion_passed_along = True;
2487 } else {
2488 if (motion_passed_along && !motif_protocol) {
2489 send_leave_message_to_toplevel(win, xclient);
2490 }
2491 motion_passed_along = False;
2492 }
2493 break;
2494 case DropEvent:
2495 if (JNU_IsNull(env, target_component)) {
2496 forward_client_message_to_toplevel(win, xclient);
2497 /* The last chance to cleanup. */
2498 awt_dnd_cleanup();
2499 }
2500 motion_passed_along = False;
2501 break;
2502 case EnterEvent:
2503 case LeaveEvent:
2504 if (motif_protocol) {
2505 forward_client_message_to_toplevel(win, xclient);
2506 }
2507 motion_passed_along = False;
2508 break;
2509 }
2510 }
2511 }
2512
2513 if (postponed_leave) {
2514 XUngrabServer(awt_display);
2515 }
2516 }
2517}
2518
2519/*
2520 * Returns True if the event is processed and shouldn't be passed along to Java.
2521 * Otherwise, return False.
2522 */
2523Boolean
2524awt_dnd_dt_process_event(XEvent* event) {
2525 Display* dpy = event->xany.display;
2526 EventStatus status = EventFailure;
2527 EventType type = UnknownEvent;
2528
2529 if (event->type == DestroyNotify) {
2530 if (event->xany.window == source_window) {
2531 awt_dnd_cleanup();
2532 }
2533 /* pass along */
2534 return False;
2535 }
2536
2537 if (event->type == PropertyNotify) {
2538 if (is_embedding_toplevel(event->xany.window)) {
2539 Atom atom = event->xproperty.atom;
2540 /*
2541 * If some other client replaced the XDnD or Motif DnD proxy with
2542 * another window we set the proxy back to the awt_root_window
2543 * and update the entry in the embedded_drop_site_list.
2544 * This code is needed, as for example Netscape 4.7 resets the proxy
2545 * when the browser shell is resized.
2546 */
2547 if (atom == _XA_MOTIF_DRAG_RECEIVER_INFO) {
2548 Window prev_motif_proxy;
2549 ProxyRegistrationStatus status;
2550 status = set_motif_proxy(event->xany.display, event->xany.window,
2551 get_awt_root_window(), &prev_motif_proxy);
2552 if (status != RegFailure && status != RegAlreadyRegistered) {
2553 set_motif_proxy_for_toplevel(event->xany.window,
2554 prev_motif_proxy,
2555 status == RegOverride);
2556 }
2557 }
2558
2559 if (atom == XA_XdndAware || atom == XA_XdndProxy) {
2560 Window prev_xdnd_proxy;
2561 unsigned int prev_protocol_version;
2562 ProxyRegistrationStatus status;
2563 status = set_xdnd_proxy(event->xany.display, event->xany.window,
2564 get_awt_root_window(), &prev_xdnd_proxy,
2565 &prev_protocol_version);
2566 if (status != RegFailure && status != RegAlreadyRegistered) {
2567 set_xdnd_proxy_for_toplevel(event->xany.window,
2568 prev_xdnd_proxy,
2569 prev_protocol_version,
2570 status == RegOverride);
2571 }
2572 }
2573 }
2574 /* pass along */
2575 return False;
2576 }
2577
2578 if (event->type != ClientMessage) {
2579 return False;
2580 }
2581
2582 if (get_component_for_window(event->xany.window) == NULL &&
2583 !is_embedding_toplevel(event->xany.window)) {
2584 return False;
2585 }
2586
2587 if (motif_top_level_leave_postponed) {
2588 /* Sanity check. */
2589 if (source_protocol != MOTIF_DND_PROTOCOL) {
2590 DTRACE_PRINTLN2("%s:%d TOP_LEVEL_LEAVE rejected - invalid state.",
2591 __FILE__, __LINE__);
2592 awt_dnd_cleanup();
2593 } else if (event->xclient.message_type ==
2594 _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
2595 unsigned char first_byte = event->xclient.data.b[0];
2596 unsigned char reason = first_byte & MOTIF_MESSAGE_REASON_MASK;
2597 unsigned char origin = first_byte & MOTIF_MESSAGE_SENDER_MASK;
2598
2599 if (origin == MOTIF_MESSAGE_FROM_INITIATOR &&
2600 reason != DROP_START) {
2601 awt_dnd_cleanup();
2602 }
2603 } else {
2604 awt_dnd_cleanup();
2605 }
2606 }
2607
2608 if (event->xclient.message_type == XA_XdndEnter) {
2609 status = handle_xdnd_enter(&event->xclient);
2610 type = EnterEvent;
2611 } else if (event->xclient.message_type == XA_XdndPosition) {
2612 status = handle_xdnd_position(&event->xclient);
2613 type = MotionEvent;
2614 } else if (event->xclient.message_type == XA_XdndLeave) {
2615 status = handle_xdnd_leave(&event->xclient);
2616 type = LeaveEvent;
2617 } else if (event->xclient.message_type == XA_XdndDrop) {
2618 status = handle_xdnd_drop(&event->xclient);
2619 type = DropEvent;
2620 } else if (event->xclient.message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
2621 unsigned char reason = event->xclient.data.b[0] & MOTIF_MESSAGE_REASON_MASK;
2622 unsigned char origin = event->xclient.data.b[0] & MOTIF_MESSAGE_SENDER_MASK;
2623
2624 /* Only initiator messages should be handled. */
2625 if (origin == MOTIF_MESSAGE_FROM_INITIATOR) {
2626 switch (reason) {
2627 case DRAG_MOTION:
2628 case OPERATION_CHANGED:
2629 status = handle_motif_drag_motion(&event->xclient);
2630 type = MotionEvent;
2631 break;
2632 case TOP_LEVEL_ENTER:
2633 status = handle_motif_top_level_enter(&event->xclient);
2634 type = EnterEvent;
2635 break;
2636 case TOP_LEVEL_LEAVE:
2637 status = handle_motif_top_level_leave(&event->xclient);
2638 type = LeaveEvent;
2639 break;
2640 case DROP_START:
2641 status = handle_motif_drop_start(&event->xclient);
2642 type = DropEvent;
2643 break;
2644 }
2645 }
2646 } else {
2647 /* Unknown message type. */
2648 return False;
2649 }
2650
2651 /*
2652 * We need to handle a special case here: Motif DnD protocol prescribed that
2653 * DROP_START message should always be preceeded with TOP_LEVEL_LEAVE
2654 * message. We need to cleanup on TOP_LEVEL_LEAVE message, but DROP_START
2655 * wouldn't be processed properly. Instead we postpone the cleanup and
2656 * send a dummy client message to ourselves. If dummy arrives first we do a
2657 * normal cleanup. If DROP_START arrives before the dummy we discard delayed
2658 * cleanup.
2659 * In case of forwarding events from an embedded Java app to an embedding
2660 * Java app it could happen that the embedding app receives the dummy before
2661 * the DROP_START message arrives from the embedding app. In this case the
2662 * drop operation on the embedding app fails to complete.
2663 * To resolve this problem we postpone forwarding of TOP_LEVEL_LEAVE message
2664 * until the next client message is about to be forwarded.
2665 */
2666 if (motif_top_level_leave_postponed && type == LeaveEvent) {
2667 /* motif_top_level_leave_postponed can be set only if the latest client
2668 message has been processed successfully. */
2669 DASSERT(status == EventSuccess);
2670 memcpy(&motif_top_level_leave_postponed_event, &event->xclient,
2671 sizeof(XClientMessageEvent));
2672 } else {
2673 post_process_client_message(&event->xclient, status, type);
2674 }
2675
2676 return True;
2677}
2678
2679static Boolean
2680register_xdnd_drop_site(Display* dpy, Window toplevel, Window window) {
2681 unsigned char ret;
2682 Atom version_atom = XDND_PROTOCOL_VERSION;
2683
2684 ret = checked_XChangeProperty(dpy, window, XA_XdndAware, XA_ATOM, 32,
2685 PropModeReplace,
2686 (unsigned char*)&version_atom, 1);
2687
2688 return (ret == Success);
2689}
2690
2691static Boolean
2692register_motif_drop_site(Display* dpy, Window toplevel, Window window) {
2693 unsigned char status;
2694 size_t data_size = MOTIF_RECEIVER_INFO_SIZE;
2695 char* data = malloc(data_size);
2696 void* p = data;
2697
2698 if (data == NULL) {
2699 DTRACE_PRINTLN2("%s:%d malloc failed.", __FILE__, __LINE__);
2700 return False;
2701 }
2702
2703 write_card8(&p, MOTIF_BYTE_ORDER);
2704 write_card8(&p, MOTIF_DND_PROTOCOL_VERSION); /* protocol version */
2705 write_card8(&p, MOTIF_DYNAMIC_STYLE); /* protocol style */
2706 write_card8(&p, 0); /* pad */
2707 write_card32(&p, window); /* proxy window */
2708 write_card16(&p, 0); /* num_drop_sites */
2709 write_card16(&p, 0); /* pad */
2710 write_card32(&p, data_size);
2711
2712 status = checked_XChangeProperty(dpy, window, _XA_MOTIF_DRAG_RECEIVER_INFO,
2713 _XA_MOTIF_DRAG_RECEIVER_INFO, 8, PropModeReplace,
2714 (unsigned char*)data, data_size);
2715
2716 free(data);
2717
2718 return (status == Success);
2719}
2720
2721static Window
2722find_toplevel_window(Display* dpy, Window window) {
2723 Window ret = None;
2724 Window root = None;
2725 Window parent = None;
2726 Window *children;
2727 unsigned int nchildren;
2728
2729 int status;
2730
2731 Atom type;
2732 int format;
2733 unsigned long nitems;
2734 unsigned long after;
2735 unsigned char *data;
2736
2737 /* Traverse the ancestor tree from window up to the root and find
2738 the top-level client window nearest to the root. */
2739 do {
2740 type = None;
2741
2742 data = NULL;
2743 status = XGetWindowProperty(dpy, window, XA_WM_STATE, 0, 0, False,
2744 AnyPropertyType, &type, &format, &nitems,
2745 &after, &data);
2746
2747 if (status == Success) {
2748 XFree(data);
2749 }
2750
2751 if (type != None) {
2752 ret = window;
2753 }
2754
2755 if (!XQueryTree(dpy, window, &root, &parent, &children, &nchildren)) {
2756 return None;
2757 }
2758
2759 XFree(children);
2760
2761 window = parent;
2762 } while (window != root);
2763
2764 return ret;
2765}
2766
2767static Boolean
2768register_drop_site(Widget outer_canvas, XtPointer componentRef) {
2769 Display* dpy = XtDisplay(outer_canvas);
2770 Widget shell = NULL;
2771 /* Shell window. */
2772 Window window = None;
2773 Window root = None;
2774 Window toplevel = None;
2775
2776 for (shell = outer_canvas; shell != NULL && !XtIsShell(shell);
2777 shell = XtParent(shell));
2778
2779 if (shell == NULL || !XtIsRealized(shell)) {
2780 DTRACE_PRINTLN2("%s:%d Cannot find a realized shell for the widget.",
2781 __FILE__, __LINE__);
2782 return False;
2783 }
2784
2785 window = XtWindow(shell);
2786
2787 if (!awt_dnd_init(dpy)) {
2788 DTRACE_PRINTLN2("%s:%d Fail to initialize.", __FILE__, __LINE__);
2789 return False;
2790 }
2791
2792 {
2793 XWindowAttributes xwa;
2794
2795 if (!XGetWindowAttributes(dpy, window, &xwa)) {
2796 DTRACE_PRINTLN2("%s:%d XGetWindowAttributes failed.", __FILE__, __LINE__);
2797 return False;
2798 }
2799
2800 root = xwa.root;
2801
2802 if (root == None) {
2803 DTRACE_PRINTLN2("%s:%d Bad root.", __FILE__, __LINE__);
2804 return False;
2805 }
2806 }
2807
2808 toplevel = find_toplevel_window(dpy, window);
2809
2810 /*
2811 * No window with WM_STATE property is found.
2812 * Since the window can be a plugin window reparented to the browser
2813 * toplevel, we cannot determine which window will eventually have WM_STATE
2814 * property set. So we schedule a timer callback that will periodically
2815 * attempt to find an ancestor with WM_STATE and register the drop site
2816 * appropriately.
2817 */
2818 if (toplevel == None) {
2819 add_delayed_registration_entry(outer_canvas, componentRef);
2820 return False;
2821 }
2822
2823 if (toplevel == window) {
2824 Boolean xdnd_registered = False;
2825 Boolean motif_registered = False;
2826
2827 xdnd_registered = register_xdnd_drop_site(dpy, toplevel, window);
2828
2829 motif_registered = register_motif_drop_site(dpy, toplevel, window);
2830
2831 if (!xdnd_registered && !motif_registered) {
2832 DTRACE_PRINTLN2("%s:%d Failed to register.", __FILE__, __LINE__);
2833 return False;
2834 }
2835 } else {
2836 if (!add_to_embedded_drop_site_list(dpy, root, toplevel, window)) {
2837 DTRACE_PRINTLN2("%s:%d Failed to init proxy.", __FILE__, __LINE__);
2838 return False;
2839 }
2840 }
2841
2842 /* There is no need to update the window for the component later, since the
2843 window is destroyed only when the component is disposed in which case the
2844 drop site will be unregistered as well. */
2845 if (add_to_drop_site_list(window, root, toplevel, XtWindow(outer_canvas),
2846 (jobject)componentRef)) {
2847 DTRACE_PRINTLN2("%s:%d Drop site registered.", __FILE__, __LINE__);
2848 return True;
2849 } else {
2850 DTRACE_PRINTLN2("%s:%d Failed to register.", __FILE__, __LINE__);
2851 return False;
2852 }
2853}
2854
2855static void
2856register_drop_site_when_realized(Widget outer_canvas, XtPointer client_data,
2857 XEvent *event, Boolean *dontSwallow) {
2858 if (XtIsRealized(outer_canvas)) {
2859 XtRemoveEventHandler(outer_canvas, StructureNotifyMask, False,
2860 register_drop_site_when_realized, client_data);
2861
2862 register_drop_site(outer_canvas, client_data);
2863 }
2864}
2865
2866/*
2867 * Registers the top-level Window that contains the specified widget as a drop
2868 * site that supports XDnD and Motif DnD protocols.
2869 * If the registration fails for some reason, adds an event handler that will
2870 * attempt to register the drop site later.
2871 *
2872 * Returns True if the drop site is registered successfully.
2873 */
2874static Boolean
2875awt_dnd_register_drop_site(Widget outer_canvas, XtPointer componentRef) {
2876 if (XtIsRealized(outer_canvas)) {
2877 return register_drop_site(outer_canvas, componentRef);
2878 } else {
2879 XtAddEventHandler(outer_canvas, StructureNotifyMask, False,
2880 register_drop_site_when_realized,
2881 componentRef);
2882
2883 DTRACE_PRINTLN2("%s:%d Unrealized shell. Register later.",
2884 __FILE__, __LINE__);
2885
2886 return True;
2887 }
2888}
2889
2890/*
2891 * Unregisters the drop site associated with the top-level Window that contains
2892 * the specified widget .
2893 *
2894 * Returns True if completes successfully, False otherwise.
2895 */
2896static Boolean
2897awt_dnd_unregister_drop_site(Widget outer_canvas, XtPointer componentRef) {
2898 Widget shell = NULL;
2899
2900 XtRemoveEventHandler(outer_canvas, StructureNotifyMask, False,
2901 register_drop_site_when_realized, componentRef);
2902
2903 remove_delayed_registration_entry(outer_canvas);
2904
2905 for (shell = outer_canvas; shell != NULL && !XtIsShell(shell);
2906 shell = XtParent(shell));
2907
2908 if (shell != NULL && XtIsShell(shell) && XtIsRealized(shell)) {
2909 Window win = XtWindow(shell);
2910 Window toplevel = get_toplevel_for_window(win);
2911 /*
2912 * Cleanup the global state if this drop site participate in the current
2913 * drag operation. Particularly, this allows to delete global ref to the
2914 * component safely.
2915 */
2916 if (get_component_for_window(win) == target_component) {
2917 awt_dnd_cleanup();
2918 }
2919 if (toplevel != win) {
2920 remove_from_embedded_drop_site_list(awt_display, toplevel, win);
2921 }
2922 return remove_from_drop_site_list(win);
2923 }
2924
2925 return True;
2926}
2927
2928/**************************** XEmbed server DnD support ***********************/
2929
2930/*
2931 *
2932 *
2933 */
2934Boolean
2935register_xembed_drop_site(JNIEnv* env, Display* dpy, jobject server,
2936 Window serverHandle, Window clientHandle) {
2937 Atom type;
2938 int format;
2939 unsigned long nitems;
2940 unsigned long after;
2941 unsigned char* data;
2942 unsigned char ret;
2943 unsigned int protocol_version;
2944
2945 Window xdnd_proxy = None;
2946 unsigned int xdnd_protocol_version = 0;
2947 Boolean xdnd_override = False;
2948
2949 if (!awt_dnd_init(dpy)) {
2950 DTRACE_PRINTLN2("%s:%d Fail to initialize.", __FILE__, __LINE__);
2951 return False;
2952 }
2953
2954 /* Get the XDnD protocol version and XDnD proxy of the XEmbed client. */
2955 data = NULL;
2956 ret = checked_XGetWindowProperty(dpy, clientHandle, XA_XdndAware, 0, 1,
2957 False, AnyPropertyType, &type, &format,
2958 &nitems, &after, &data);
2959
2960 /* XEmbed client doesn't have an associated XDnD drop site -
2961 do nothing and return True to indicate success. */
2962 if (ret != Success || data == NULL || nitems == 0 || type != XA_ATOM) {
2963 XFree(data);
2964 return False;
2965 }
2966
2967 protocol_version = *((unsigned int*)data);
2968
2969 XFree(data);
2970
2971 if (protocol_version < XDND_MIN_PROTOCOL_VERSION) {
2972 return False;
2973 }
2974
2975 xdnd_protocol_version = protocol_version;
2976
2977 /* XdndProxy is not supported prior to XDnD version 4 */
2978 if (protocol_version >= 4) {
2979 int status;
2980
2981 data = NULL;
2982 status = XGetWindowProperty(dpy, clientHandle, XA_XdndProxy, 0, 1,
2983 False, XA_WINDOW, &type, &format,
2984 &nitems, &after, &data);
2985
2986 if (status == Success && data != NULL && type == XA_WINDOW) {
2987 xdnd_proxy = *((Window*)data);
2988
2989 if (xdnd_proxy != None) {
2990 XFree(data);
2991
2992 data = NULL;
2993 status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndProxy,
2994 0, 1, False, XA_WINDOW, &type,
2995 &format, &nitems, &after,
2996 &data);
2997
2998 if (status != Success || data == NULL || type != XA_WINDOW ||
2999 *((Window*)data) != xdnd_proxy) {
3000 /* Ignore invalid proxy. */
3001 xdnd_proxy = None;
3002 }
3003 }
3004
3005 if (xdnd_proxy != None) {
3006 XFree(data);
3007
3008 data = NULL;
3009 status = XGetWindowProperty(dpy, xdnd_proxy, XA_XdndAware, 0, 1,
3010 False, AnyPropertyType, &type,
3011 &format, &nitems, &after, &data);
3012
3013 if (status == Success && data != NULL && type == XA_ATOM) {
3014 unsigned int proxy_version = *((unsigned int*)data);
3015
3016 if (proxy_version != protocol_version) {
3017 /* Ignore invalid proxy. */
3018 xdnd_proxy = None;
3019 }
3020 } else {
3021 /* Ignore invalid proxy. */
3022 xdnd_proxy = None;
3023 }
3024 }
3025 }
3026
3027 XFree(data);
3028 }
3029
3030 set_xembed_drop_target(env, server);
3031
3032 /* Add protocol specific entries for the embedded window. */
3033 /* Only XDnD protocol is supported for XEmbed clients. */
3034 {
3035 EmbeddedDropSiteProtocolListEntry* xdnd_entry = NULL;
3036
3037 xdnd_entry = malloc(sizeof(EmbeddedDropSiteProtocolListEntry));
3038
3039 if (xdnd_entry == NULL) {
3040 return False;
3041 }
3042
3043 xdnd_entry->window = clientHandle;
3044 xdnd_entry->proxy = xdnd_proxy;
3045 xdnd_entry->protocol_version = xdnd_protocol_version;
3046 xdnd_entry->overriden = True;
3047 xdnd_entry->next = embedded_xdnd_protocol_list;
3048 embedded_xdnd_protocol_list = xdnd_entry;
3049 }
3050
3051 {
3052 EmbeddedDropSiteListEntry* entry = NULL;
3053 Window* sites = NULL;
3054
3055 entry = malloc(sizeof(EmbeddedDropSiteListEntry));
3056
3057 if (entry == NULL) {
3058 return False;
3059 }
3060
3061 sites = malloc(sizeof(Window));
3062
3063 if (sites == NULL) {
3064 free(entry);
3065 return False;
3066 }
3067
3068 sites[0] = clientHandle;
3069
3070 entry->toplevel = serverHandle;
3071 entry->root = None;
3072 entry->event_mask = 0;
3073 entry->embedded_sites_count = 1;
3074 entry->embedded_sites = sites;
3075 entry->next = embedded_drop_site_list;
3076 embedded_drop_site_list = entry;
3077 }
3078
3079 return True;
3080}
3081
3082Boolean
3083unregister_xembed_drop_site(JNIEnv* env, Display* dpy, jobject server,
3084 Window serverHandle, Window clientHandle) {
3085 remove_from_embedded_drop_site_list(dpy, serverHandle, clientHandle);
3086 return True;
3087}
3088
3089void
3090forward_event_to_embedded(Window embedded, jlong ctxt, jint eventID) {
3091 static XClientMessageEvent* prevMessage = NULL;
3092 static Boolean overXEmbedClient = False;
3093
3094 XClientMessageEvent* xclient =
3095 (XClientMessageEvent*)jlong_to_ptr(ctxt);
3096
3097 if (xclient == NULL && prevMessage == NULL) {
3098 return;
3099 }
3100
3101 if (xclient != NULL) {
3102 /*
3103 * NOTE: this check guarantees that prevMessage will always be an XDnD
3104 * drag message.
3105 */
3106 if (!is_xdnd_drag_message_type(xclient->message_type)) {
3107 return;
3108 }
3109
3110 if (!overXEmbedClient) {
3111 long* appended_data = jlong_to_ptr(ctxt) +
3112 sizeof(XClientMessageEvent);
3113
3114 /* Copy XdndTypeList from source to proxy. */
3115 if ((appended_data[0] & XDND_DATA_TYPES_BIT) != 0) {
3116 unsigned char ret;
3117 Atom type;
3118 int format;
3119 unsigned long nitems;
3120 unsigned long after;
3121 unsigned char *data;
3122
3123 data = NULL;
3124 ret = checked_XGetWindowProperty(xclient->display,
3125 xclient->data.l[0],
3126 XA_XdndTypeList, 0, 0xFFFF,
3127 False, XA_ATOM, &type, &format,
3128 &nitems, &after, &data);
3129
3130 /* Ignore the source if the window is destroyed. */
3131 if (ret == BadWindow) {
3132 return;
3133 }
3134
3135 if (ret == Success) {
3136 if (type == XA_ATOM && format == 32) {
3137 ret = checked_XChangeProperty(xclient->display,
3138 xclient->window,
3139 XA_XdndTypeList, XA_ATOM,
3140 32, PropModeReplace, data,
3141 nitems);
3142 }
3143
3144 XFree(data);
3145 }
3146 }
3147
3148 set_proxy_mode_source_window(xclient->data.l[0]);
3149
3150 {
3151 XClientMessageEvent enter;
3152 enter.display = xclient->display;
3153 enter.type = ClientMessage;
3154 enter.window = embedded;
3155 enter.format = 32;
3156 enter.message_type = XA_XdndEnter;
3157
3158 enter.data.l[0] = xclient->window; /* XID of the source window */
3159 enter.data.l[1] = appended_data[0];
3160 enter.data.l[2] = appended_data[1];
3161 enter.data.l[3] = appended_data[2];
3162 enter.data.l[4] = appended_data[3];
3163
3164 forward_client_message_to_toplevel(embedded, &enter);
3165 }
3166
3167 overXEmbedClient = True;
3168 }
3169
3170 /* Make a copy of the original event, since we are going to modify the
3171 event while it still can be referenced from other Java events. */
3172 {
3173 XClientMessageEvent copy;
3174 memcpy(&copy, xclient, sizeof(XClientMessageEvent));
3175 copy.data.l[0] = xclient->window;
3176
3177 forward_client_message_to_toplevel(embedded, &copy);
3178 }
3179 }
3180
3181 if (eventID == java_awt_event_MouseEvent_MOUSE_EXITED) {
3182 if (overXEmbedClient) {
3183 if (xclient != NULL || prevMessage != NULL) {
3184 /* Last chance to send XdndLeave to the XEmbed client. */
3185 XClientMessageEvent leave;
3186
3187 leave.display = xclient != NULL ?
3188 xclient->display : prevMessage->display;
3189 leave.type = ClientMessage;
3190 leave.window = embedded;
3191 leave.format = 32;
3192 leave.message_type = XA_XdndLeave;
3193 leave.data.l[0] = xclient != NULL ?
3194 xclient->window : prevMessage->window; /* XID of the source window */
3195 leave.data.l[1] = 0; /* flags */
3196
3197 forward_client_message_to_toplevel(embedded, &leave);
3198 }
3199 overXEmbedClient = False;
3200 }
3201 }
3202
3203 if (eventID == java_awt_event_MouseEvent_MOUSE_RELEASED) {
3204 overXEmbedClient = False;
3205 awt_dnd_cleanup();
3206 }
3207
3208 if (prevMessage != 0) {
3209 free(prevMessage);
3210 prevMessage = 0;
3211 }
3212
3213 if (xclient != 0 && overXEmbedClient) {
3214 prevMessage = malloc(sizeof(XClientMessageEvent));
3215
3216 memcpy(prevMessage, xclient, sizeof(XClientMessageEvent));
3217 }
3218}
3219
3220/******************************************************************************/
3221
3222/*
3223 * Class: sun_awt_motif_MWindowPeer
3224 * Method: registerX11DropTarget
3225 * Signature: (Ljava/awt/Component;)V
3226 */
3227
3228JNIEXPORT void JNICALL
3229Java_sun_awt_motif_MWindowPeer_registerX11DropTarget(JNIEnv *env, jobject this,
3230 jobject target) {
3231 struct FrameData* wdata = NULL;
3232 DropSitePtr dsi = NULL;
3233
3234 wdata = (struct FrameData *)
3235 JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData);
3236
3237 if (wdata == NULL || wdata->winData.comp.widget == NULL) {
3238 JNU_ThrowNullPointerException(env, "NULL component data");
3239 return;
3240 }
3241
3242 if (wdata->winData.shell == NULL) {
3243 JNU_ThrowNullPointerException(env, "Null shell widget");
3244 return;
3245 }
3246
3247 DASSERT(wdata->winData.comp.dsi == NULL);
3248
3249 dsi = (DropSitePtr)calloc(1, sizeof(struct DropSiteInfo));
3250
3251 if (dsi == NULL) {
3252 JNU_ThrowOutOfMemoryError(env, "");
3253 return;
3254 }
3255
3256 dsi->component = (*env)->NewGlobalRef(env, target);
3257 dsi->isComposite = False;
3258
3259 wdata->winData.comp.dsi = dsi;
3260
3261 AWT_LOCK();
3262
3263 awt_dnd_register_drop_site(wdata->winData.comp.widget,
3264 dsi->component);
3265
3266 AWT_UNLOCK();
3267}
3268
3269/*
3270 * Class: sun_awt_motif_MWindowPeer
3271 * Method: unregisterX11DropTarget
3272 * Signature: (Ljava/awt/Component;)V
3273 */
3274
3275JNIEXPORT void JNICALL
3276Java_sun_awt_motif_MWindowPeer_unregisterX11DropTarget(JNIEnv *env,
3277 jobject this,
3278 jobject target) {
3279 struct FrameData* wdata = NULL;
3280 DropSitePtr dsi = NULL;
3281
3282 wdata = (struct FrameData *)
3283 JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData);
3284
3285 if (wdata == NULL) {
3286 JNU_ThrowNullPointerException(env, "Null component data");
3287 return;
3288 }
3289
3290 if (wdata->winData.shell == NULL) {
3291 JNU_ThrowNullPointerException(env, "Null shell widget");
3292 return;
3293 }
3294
3295 dsi = wdata->winData.comp.dsi;
3296
3297 if (dsi == NULL) {
3298 JNU_ThrowNullPointerException(env, "Null DropSiteInfo");
3299 return;
3300 }
3301
3302 AWT_LOCK();
3303
3304 awt_dnd_unregister_drop_site(wdata->winData.comp.widget, dsi->component);
3305
3306 AWT_UNLOCK();
3307
3308 wdata->winData.comp.dsi = NULL;
3309
3310 (*env)->DeleteGlobalRef(env, dsi->component);
3311
3312 free(dsi);
3313}
3314
3315static void
3316dt_send_event_to_source(XClientMessageEvent* xclient) {
3317 /* Shortcut if the source is in the same JVM. */
3318 if (xclient->window == awt_dnd_ds_get_source_window()) {
3319 awt_dnd_ds_process_event((XEvent*)xclient);
3320 } else {
3321 unsigned char ret;
3322
3323 ret = checked_XSendEvent(xclient->display, xclient->window, False,
3324 NoEventMask, (XEvent*)xclient);
3325
3326 if (ret == BadWindow) {
3327 DTRACE_PRINTLN2("%s:%d XSendEvent - invalid window.",
3328 __FILE__, __LINE__);
3329
3330 /* Cleanup if we are still communicating with this window. */
3331 if (source_window == xclient->window) {
3332 awt_dnd_cleanup();
3333 }
3334 }
3335 }
3336}
3337
3338static void
3339dt_send_response(XClientMessageEvent* xclient, jint eventID, jint action) {
3340 Display* dpy = xclient->display;
3341 XClientMessageEvent response;
3342
3343 if (xclient->message_type == XA_XdndPosition) {
3344 long* event_data = xclient->data.l;
3345
3346 if (eventID == java_awt_event_MouseEvent_MOUSE_EXITED) {
3347 action = java_awt_dnd_DnDConstants_ACTION_NONE;
3348 }
3349
3350 response.display = dpy;
3351 response.type = ClientMessage;
3352 response.window = event_data[0];
3353 response.format = 32;
3354 response.message_type = XA_XdndStatus;
3355 /* target window */
3356 response.data.l[0] = xclient->window;
3357 /* flags */
3358 response.data.l[1] = 0;
3359 if (action != java_awt_dnd_DnDConstants_ACTION_NONE) {
3360 response.data.l[1] |= XDND_ACCEPT_DROP_FLAG;
3361 }
3362 /* specify an empty rectangle */
3363 response.data.l[2] = 0; /* x, y */
3364 response.data.l[3] = 0; /* w, h */
3365 /* action accepted by the target */
3366 response.data.l[4] = java_to_xdnd_action(action);
3367 } else if (xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
3368 int reason = (int)(xclient->data.b[0] & MOTIF_MESSAGE_REASON_MASK);
3369 int origin = (int)(xclient->data.b[0] & MOTIF_MESSAGE_SENDER_MASK);
3370 unsigned char byte_order = xclient->data.b[1];
3371 CARD16 response_flags = 0;
3372 CARD8 response_reason = 0;
3373 void* p = &response.data.b;
3374
3375 /* Only initiator messages should be handled. */
3376 if (origin != MOTIF_MESSAGE_FROM_INITIATOR) {
3377 DTRACE_PRINTLN2("%s:%d Receiver message.", __FILE__, __LINE__);
3378 return;
3379 }
3380
3381 switch (reason) {
3382 case DRAG_MOTION:
3383 switch (eventID) {
3384 case java_awt_event_MouseEvent_MOUSE_ENTERED:
3385 response_reason = DROP_SITE_ENTER;
3386 break;
3387 case java_awt_event_MouseEvent_MOUSE_DRAGGED:
3388 response_reason = DRAG_MOTION;
3389 break;
3390 case java_awt_event_MouseEvent_MOUSE_EXITED:
3391 response_reason = DROP_SITE_LEAVE;
3392 break;
3393 }
3394 }
3395
3396 response.display = dpy;
3397 response.type = ClientMessage;
3398 response.window = read_card32(xclient->data.b, 12, byte_order);
3399 response.format = 8;
3400 response.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
3401
3402 write_card8(&p, response_reason | MOTIF_MESSAGE_FROM_RECEIVER);
3403 write_card8(&p, MOTIF_BYTE_ORDER);
3404
3405 if (response_reason != DROP_SITE_LEAVE) {
3406 CARD16 flags = read_card16(xclient->data.b, 2, byte_order);
3407 unsigned char drop_site_status =
3408 (action == java_awt_dnd_DnDConstants_ACTION_NONE) ?
3409 MOTIF_INVALID_DROP_SITE : MOTIF_VALID_DROP_SITE;
3410
3411 /* Clear action and drop site status bits. */
3412 response_flags =
3413 flags & ~MOTIF_DND_ACTION_MASK & ~MOTIF_DND_STATUS_MASK;
3414
3415 /* Fill in new action and drop site status. */
3416 response_flags |=
3417 java_to_motif_actions(action) << MOTIF_DND_ACTION_SHIFT;
3418 response_flags |=
3419 drop_site_status << MOTIF_DND_STATUS_SHIFT;
3420 } else {
3421 response_flags = 0;
3422 }
3423
3424 write_card16(&p, response_flags);
3425
3426 /* Write time stamp. */
3427 write_card32(&p, read_card32(xclient->data.b, 4, byte_order));
3428
3429 /* Write coordinates. */
3430 if (response_reason != DROP_SITE_LEAVE) {
3431 write_card16(&p, read_card16(xclient->data.b, 8, byte_order));
3432 write_card16(&p, read_card16(xclient->data.b, 10, byte_order));
3433 } else {
3434 write_card16(&p, 0);
3435 write_card16(&p, 0);
3436 }
3437 } else {
3438 return;
3439 }
3440
3441 dt_send_event_to_source(&response);
3442}
3443
3444static void
3445dummy_selection_callback(Widget w, XtPointer client_data, Atom* selection,
3446 Atom* type, XtPointer value, unsigned long *length,
3447 int32_t *format) {
3448 /* The selection callback is responsible for freeing the data. */
3449 if (value != NULL) {
3450 XtFree(value);
3451 value = NULL;
3452 }
3453}
3454
3455static void
3456dt_notify_drop_done(JNIEnv* env, XClientMessageEvent* xclient, jboolean success,
3457 jint action) {
3458 if (xclient->message_type == XA_XdndDrop) {
3459 Display* dpy = xclient->display;
3460 XClientMessageEvent finished;
3461 long* event_data = xclient->data.l;
3462
3463 /*
3464 * The XDnD protocol recommends that the target requests the special
3465 * target DELETE in case if the drop action is XdndActionMove.
3466 */
3467 if (action == java_awt_dnd_DnDConstants_ACTION_MOVE &&
3468 success == JNI_TRUE) {
3469
3470 Time time_stamp = event_data[2];
3471
3472 XtGetSelectionValue(awt_root_shell, XA_XdndSelection, XA_DELETE,
3473 dummy_selection_callback, NULL, time_stamp);
3474 }
3475
3476 finished.display = dpy;
3477 finished.type = ClientMessage;
3478 finished.window = event_data[0];
3479 finished.format = 32;
3480 finished.message_type = XA_XdndFinished;
3481 finished.data.l[0] = xclient->window;
3482 finished.data.l[1] = 0; /* flags */
3483 finished.data.l[2] = None;
3484 if (source_protocol_version >= 5) {
3485 if (success == JNI_TRUE) {
3486 finished.data.l[1] |= XDND_ACCEPT_DROP_FLAG;
3487 }
3488 finished.data.l[2] = java_to_xdnd_action(action);
3489 }
3490
3491 dt_send_event_to_source(&finished);
3492 } else if (xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
3493 char* event_data = xclient->data.b;
3494 unsigned char event_byte_order = read_card8(event_data, 1);
3495 unsigned char first_byte = read_card8(event_data, 0);
3496 unsigned char reason = first_byte & MOTIF_MESSAGE_REASON_MASK;
3497 unsigned char origin = first_byte & MOTIF_MESSAGE_SENDER_MASK;
3498 Atom selection = None;
3499 Time time_stamp = CurrentTime;
3500 Atom status_atom = None;
3501
3502 if (origin != MOTIF_MESSAGE_FROM_INITIATOR) {
3503 DTRACE_PRINTLN2("%s:%d Invalid origin.", __FILE__, __LINE__);
3504 return;
3505 }
3506
3507 if (reason != DROP_START) {
3508 DTRACE_PRINTLN2("%s:%d Invalid reason.", __FILE__, __LINE__);
3509 return;
3510 }
3511
3512 selection = read_card32(event_data, 12, event_byte_order);
3513 time_stamp = read_card32(event_data, 4, event_byte_order);
3514
3515 if (success == JNI_TRUE) {
3516 status_atom = XA_XmTRANSFER_SUCCESS;
3517 } else {
3518 status_atom = XA_XmTRANSFER_FAILURE;
3519 }
3520
3521 /*
3522 * This is just the way to communicate the drop completion status back
3523 * to the initiator as prescribed by the Motif DnD protocol.
3524 */
3525 XtGetSelectionValue(awt_root_shell, selection, status_atom,
3526 dummy_selection_callback, NULL, time_stamp);
3527 }
3528
3529 /*
3530 * Flush the buffer to guarantee that the drop completion event is sent
3531 * to the source before the method returns.
3532 */
3533 XFlush(awt_display);
3534
3535 /* Trick to prevent awt_dnd_cleanup() from posting dragExit */
3536 target_component = NULL;
3537 /* Cannot do cleanup before the drop finishes as we need source protocol
3538 version to send XdndFinished message. */
3539 awt_dnd_cleanup();
3540}
3541
3542/*
3543 * Class: sun_awt_motif_X11DropTargetContextPeer
3544 * Method: sendResponse
3545 * Signature: (IIJZ)V
3546 */
3547
3548JNIEXPORT void JNICALL
3549Java_sun_awt_motif_X11DropTargetContextPeer_sendResponse(JNIEnv *env,
3550 jobject this,
3551 jint eventID,
3552 jint action,
3553 jlong nativeCtxt,
3554 jboolean dispatcherDone,
3555 jboolean consumed) {
3556 XClientMessageEvent* xclient =
3557 (XClientMessageEvent*)jlong_to_ptr(nativeCtxt);
3558
3559 AWT_LOCK();
3560
3561 if (consumed == JNI_FALSE) {
3562 dt_send_response(xclient, eventID, action);
3563 }
3564
3565 /*
3566 * Free the native context only if all copies of the original event are
3567 * processed.
3568 */
3569 if (dispatcherDone == JNI_TRUE) {
3570 XtFree((char*)xclient);
3571 }
3572
3573 AWT_UNLOCK();
3574}
3575
3576/*
3577 * Class: sun_awt_motif_X11DropTargetContextPeer
3578 * Method: dropDone
3579 * Signature: (JZI)V
3580 */
3581
3582JNIEXPORT void JNICALL
3583Java_sun_awt_motif_X11DropTargetContextPeer_dropDone(JNIEnv *env,
3584 jobject this,
3585 jlong nativeCtxt,
3586 jboolean success,
3587 jint action) {
3588 XClientMessageEvent* xclient =
3589 (XClientMessageEvent*)jlong_to_ptr(nativeCtxt);
3590
3591 AWT_LOCK();
3592
3593 dt_notify_drop_done(env, xclient, success, action);
3594
3595 XtFree((char*)xclient);
3596
3597 AWT_UNLOCK();
3598}
3599
3600/*
3601 * Class: sun_awt_motif_X11DropTargetContextPeer
3602 * Method: getData
3603 * Signature: (IJ)Ljava/lang/Object;
3604 */
3605
3606JNIEXPORT jobject JNICALL
3607Java_sun_awt_motif_X11DropTargetContextPeer_getData(JNIEnv *env,
3608 jobject this,
3609 jlong nativeCtxt,
3610 jlong formatAtom) {
3611 XClientMessageEvent* xclient =
3612 (XClientMessageEvent*)jlong_to_ptr(nativeCtxt);
3613
3614 Atom selection = None;
3615 Time time_stamp = CurrentTime;
3616 Atom target = (Atom)formatAtom;
3617
3618 if (xclient->message_type == XA_XdndDrop ||
3619 xclient->message_type == XA_XdndPosition) {
3620 Display* dpy = xclient->display;
3621 Window source_win = xclient->data.l[0];
3622 Atom protocol_version = 0;
3623
3624 int status;
3625
3626 Atom type;
3627 int format;
3628 unsigned long nitems;
3629 unsigned long after;
3630 unsigned char *data;
3631
3632 AWT_LOCK();
3633
3634 data = NULL;
3635 status = XGetWindowProperty(dpy, source_win, XA_XdndAware, 0, 0xFFFF,
3636 False, XA_ATOM, &type, &format, &nitems,
3637 &after, &data);
3638
3639 if (status == Success && data != NULL && type == XA_ATOM && format == 32
3640 && nitems > 0) {
3641 protocol_version = (protocol_version > XDND_PROTOCOL_VERSION) ?
3642 XDND_PROTOCOL_VERSION : protocol_version;
3643
3644 if (protocol_version > 0) {
3645 if (xclient->message_type == XA_XdndDrop) {
3646 time_stamp = xclient->data.l[2];
3647 } else if (xclient->message_type == XA_XdndPosition) {
3648 time_stamp = xclient->data.l[3];
3649 }
3650 }
3651 }
3652
3653 if (status == Success) {
3654 XFree(data);
3655 data = NULL;
3656 }
3657
3658 AWT_FLUSH_UNLOCK();
3659
3660 selection = XA_XdndSelection;
3661 if (time_stamp == CurrentTime) {
3662 time_stamp = awt_util_getCurrentServerTime();
3663 }
3664
3665 } else if (xclient->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
3666 char* event_data = xclient->data.b;
3667 unsigned char event_byte_order = read_card8(event_data, 1);
3668 unsigned char first_byte = read_card8(event_data, 0);
3669 unsigned char reason = first_byte & MOTIF_MESSAGE_REASON_MASK;
3670 unsigned char origin = first_byte & MOTIF_MESSAGE_SENDER_MASK;
3671
3672 if (origin != MOTIF_MESSAGE_FROM_INITIATOR) {
3673 DTRACE_PRINTLN2("%s:%d Invalid origin.", __FILE__, __LINE__);
3674 return NULL;
3675 }
3676
3677 switch (reason) {
3678 case DROP_START:
3679 selection = read_card32(event_data, 12, event_byte_order);
3680 break;
3681 case DRAG_MOTION:
3682 case OPERATION_CHANGED:
3683 selection = source_atom;
3684 break;
3685 default:
3686 DTRACE_PRINTLN2("%s:%d Invalid reason.", __FILE__, __LINE__);
3687 return NULL;
3688 }
3689
3690 if (selection == None) {
3691 return NULL;
3692 }
3693
3694 time_stamp = read_card32(event_data, 4, event_byte_order);
3695 } else {
3696 return NULL;
3697 }
3698
3699 return get_selection_data(env, selection, target, time_stamp);
3700}