blob: 4edc454df82419b59b54e7bd74af984306c83e8a [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/* Declares getCursor(JNIEnv, jobject) */
33#include "awt_Cursor.h"
34
35/* Define java constants */
36#include "java_awt_dnd_DnDConstants.h"
37#include "sun_awt_dnd_SunDragSourceContextPeer.h"
38
39/* Define DECLARE_* macros */
40#include "awt_DataTransferer.h"
41
42#define GRAB_EVENT_MASK \
43 (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask)
44
45/* Events selected on the root window during drag. */
46#define ROOT_EVENT_MASK \
47 (ButtonMotionMask | KeyPressMask | KeyReleaseMask)
48
49/* Events selected on registered receiver windows during drag. */
50#define RECEIVER_EVENT_MASK \
51 (StructureNotifyMask)
52
53
54/* in canvas.c */
55extern jint getModifiers(uint32_t state, jint button, jint keyCode);
56
57typedef struct {
58 CARD8 byte_order;
59 CARD8 protocol_version;
60 CARD16 index;
61 CARD32 selection_atom;
62} InitiatorInfo;
63
64typedef enum {
65 /*
66 * Communicate with receivers of both protocols.
67 * If the receiver supports both protocols,
68 * choose Motif DnD for communication.
69 */
70 DS_POLICY_PREFER_MOTIF,
71 /*
72 * Communicate with receivers of both protocols.
73 * If the receiver supports both protocols,
74 * choose XDnD for communication. [default]
75 */
76 DS_POLICY_PREFER_XDND,
77 /* Communicate only with Motif DnD receivers. */
78 DS_POLICY_ONLY_MOTIF,
79 /* Communicate only with XDnD receivers. */
80 DS_POLICY_ONLY_XDND
81} DragSourcePolicy;
82
83
84/* The drag source policy. */
85static DragSourcePolicy drag_source_policy = DS_POLICY_PREFER_XDND;
86
87static Boolean dnd_in_progress = False;
88static Boolean drag_in_progress = False;
89static jobject source_peer = NULL;
90static Atom* data_types = NULL;
91static unsigned int data_types_count = 0;
92static Window drag_root_window = None;
93static EventMask your_root_event_mask = NoEventMask;
94static Time latest_time_stamp = CurrentTime;
95
96/* The child of the root which is currently under the mouse. */
97static Window target_root_subwindow = None;
98
99static Window target_window = None;
100static long target_window_mask = 0;
101static Window target_proxy_window = None;
102static Protocol target_protocol = NO_PROTOCOL;
103static unsigned int target_protocol_version = 0;
104/*
105 * The server time when the pointer entered the current target -
106 * needed on Motif DnD to filter out messages from the previous
107 * target.
108 * It is updated whenever the target_window is updated.
109 * If the target_window is set to non-None, it is set to the time stamp
110 * of the X event that trigger the update. Otherwise, it is set to CurrentTime.
111 */
112static Time target_enter_server_time = CurrentTime;
113
114static int x_root = 0;
115static int y_root = 0;
116static unsigned int event_state = 0;
117
118static jint source_action = java_awt_dnd_DnDConstants_ACTION_NONE;
119static jint source_actions = java_awt_dnd_DnDConstants_ACTION_NONE;
120static jint target_action = java_awt_dnd_DnDConstants_ACTION_NONE;
121
122/* Forward declarations */
123static void cleanup_drag(Display* dpy, Time time);
124static Boolean process_proxy_mode_event(XEvent* xev);
125
126/**************************** XEmbed server DnD support ***********************/
127static Window proxy_mode_source_window = None;
128/******************************************************************************/
129
130/**************************** JNI stuff ***************************************/
131
132DECLARE_JAVA_CLASS(dscp_clazz, "sun/awt/dnd/SunDragSourceContextPeer")
133
134static void
135ds_postDragSourceDragEvent(JNIEnv* env, jint targetAction, unsigned int state,
136 int x, int y, jint dispatch_type) {
137 DECLARE_VOID_JAVA_METHOD(dscp_postDragSourceDragEvent, dscp_clazz,
138 "postDragSourceDragEvent", "(IIIII)V");
139
140 DASSERT(!JNU_IsNull(env, source_peer));
141 if (JNU_IsNull(env, source_peer)) {
142 return;
143 }
144
145 (*env)->CallVoidMethod(env, source_peer, dscp_postDragSourceDragEvent,
146 targetAction, getModifiers(state, 0, 0), x, y,
147 dispatch_type);
148}
149
150static jint
151ds_convertModifiersToDropAction(JNIEnv* env, unsigned int state) {
152 jint action;
153 DECLARE_STATIC_JINT_JAVA_METHOD(dscp_convertModifiersToDropAction, dscp_clazz,
154 "convertModifiersToDropAction", "(II)I");
155 action = (*env)->CallStaticIntMethod(env, clazz, dscp_convertModifiersToDropAction,
156 getModifiers(state, 0, 0), source_actions);
157 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
158 (*env)->ExceptionDescribe(env);
159 (*env)->ExceptionClear(env);
160 return java_awt_dnd_DnDConstants_ACTION_NONE;
161 }
162 return action;
163}
164
165static void
166ds_postDragSourceEvent(JNIEnv* env, int x, int y) {
167 DECLARE_VOID_JAVA_METHOD(dscp_dragExit, dscp_clazz,
168 "dragExit", "(II)V");
169
170 DASSERT(!JNU_IsNull(env, source_peer));
171 if (JNU_IsNull(env, source_peer)) {
172 return;
173 }
174
175 (*env)->CallVoidMethod(env, source_peer, dscp_dragExit, x, y);
176}
177
178static void
179ds_postDragSourceDropEvent(JNIEnv* env, jboolean success, jint targetAction,
180 int x, int y) {
181 DECLARE_VOID_JAVA_METHOD(dscp_dragDropFinished, dscp_clazz,
182 "dragDropFinished", "(ZIII)V");
183
184 DASSERT(!JNU_IsNull(env, source_peer));
185 if (JNU_IsNull(env, source_peer)) {
186 return;
187 }
188
189 (*env)->CallVoidMethod(env, source_peer, dscp_dragDropFinished,
190 success, targetAction, x, y);
191}
192
193/******************************************************************************/
194
195static void
196cancel_drag(XtPointer client_data, XtIntervalId* id) {
197 Time time_stamp = awt_util_getCurrentServerTime();
198
199 cleanup_drag(awt_display, time_stamp);
200}
201
202#define DONT_CARE -1
203
204static void
205awt_popupCallback(Widget shell, XtPointer closure, XtPointer call_data) {
206 XtGrabKind grab_kind = XtGrabNone;
207
208 if (call_data != NULL) {
209 grab_kind = *((XtGrabKind*)call_data);
210 }
211
212 if (XmIsVendorShell(shell)) {
213 int input_mode;
214 XtVaGetValues(shell, XmNmwmInputMode, &input_mode, NULL);
215 switch (input_mode) {
216 case DONT_CARE:
217 case MWM_INPUT_MODELESS:
218 grab_kind = XtGrabNonexclusive; break;
219 case MWM_INPUT_PRIMARY_APPLICATION_MODAL:
220 case MWM_INPUT_SYSTEM_MODAL:
221 case MWM_INPUT_FULL_APPLICATION_MODAL:
222 grab_kind = XtGrabExclusive; break;
223 }
224 }
225
226 if (grab_kind == XtGrabExclusive) {
227 /*
228 * We should cancel the drag on the toolkit thread. Otherwise, it can be
229 * called while the toolkit thread is waiting inside some drag callback.
230 * In this case Motif will crash when the drag callback returns.
231 */
232 XtAppAddTimeOut(awt_appContext, 0L, cancel_drag, NULL);
233 }
234}
235
236static XtInitProc xt_shell_initialize = NULL;
237
238static void
239awt_ShellInitialize(Widget req, Widget new, ArgList args, Cardinal *num_args) {
240 XtAddCallback(new, XtNpopupCallback, awt_popupCallback, NULL);
241 (*xt_shell_initialize)(req, new, args, num_args);
242}
243
244/*
245 * Fix for 4484572 (copied from awt_XmDnD.c).
246 * Modify the 'initialize' routine for all ShellWidget instances, so that it
247 * will install an XtNpopupCallback that cancels the current drag operation.
248 * It is needed, since AWT doesn't have full control over all ShellWidget
249 * instances (e.g. XmPopupMenu internally creates and popups an XmMenuShell).
250 */
251static void
252awt_set_ShellInitialize() {
253 static Boolean inited = False;
254
255 DASSERT(!inited);
256 if (inited) {
257 return;
258 }
259
260 xt_shell_initialize = shellWidgetClass->core_class.initialize;
261 shellWidgetClass->core_class.initialize = (XtInitProc)awt_ShellInitialize;
262 inited = True;
263}
264
265/*
266 * Returns True if initialization completes successfully.
267 */
268Boolean
269awt_dnd_ds_init(Display* display) {
270 if (XSaveContext(display, XA_XdndSelection, awt_convertDataContext,
271 (XPointer)NULL) == XCNOMEM) {
272 return False;
273 }
274
275 if (XSaveContext(display, _XA_MOTIF_ATOM_0, awt_convertDataContext,
276 (XPointer)NULL) == XCNOMEM) {
277 return False;
278 }
279
280 {
281 char *ev = getenv("_JAVA_DRAG_SOURCE_POLICY");
282
283 /* By default XDnD protocol is preferred. */
284 drag_source_policy = DS_POLICY_PREFER_XDND;
285
286 if (ev != NULL) {
287 if (strcmp(ev, "PREFER_XDND") == 0) {
288 drag_source_policy = DS_POLICY_PREFER_XDND;
289 } else if (strcmp(ev, "PREFER_MOTIF") == 0) {
290 drag_source_policy = DS_POLICY_PREFER_MOTIF;
291 } else if (strcmp(ev, "ONLY_MOTIF") == 0) {
292 drag_source_policy = DS_POLICY_ONLY_MOTIF;
293 } else if (strcmp(ev, "ONLY_XDND") == 0) {
294 drag_source_policy = DS_POLICY_ONLY_XDND;
295 }
296 }
297 }
298
299 awt_set_ShellInitialize();
300
301 return True;
302}
303
304/*
305 * Returns a handle of the window used as a drag source.
306 */
307Window
308awt_dnd_ds_get_source_window() {
309 return get_awt_root_window();
310}
311
312/*
313 * Returns True if a drag operation initiated by this client
314 * is still in progress.
315 */
316Boolean
317awt_dnd_ds_in_progress() {
318 return dnd_in_progress;
319}
320
321static void
322ds_send_event_to_target(XClientMessageEvent* xclient) {
323 /* Shortcut if the source is in the same JVM. */
324 if (XtWindowToWidget(xclient->display, target_proxy_window) != NULL) {
325 awt_dnd_dt_process_event((XEvent*)xclient);
326 } else {
327 XSendEvent(xclient->display, target_proxy_window, False, NoEventMask,
328 (XEvent*)xclient);
329 }
330}
331
332static void
333xdnd_send_enter(Display* dpy, Time time) {
334 XClientMessageEvent enter;
335
336 enter.display = dpy;
337 enter.type = ClientMessage;
338 enter.window = target_window;
339 enter.format = 32;
340 enter.message_type = XA_XdndEnter;
341 enter.data.l[0] = awt_dnd_ds_get_source_window();
342 enter.data.l[1] = target_protocol_version << XDND_PROTOCOL_SHIFT;
343 enter.data.l[1] |= data_types_count > 3 ? XDND_DATA_TYPES_BIT : 0;
344 enter.data.l[2] = data_types_count > 0 ? data_types[0] : None;
345 enter.data.l[3] = data_types_count > 1 ? data_types[1] : None;
346 enter.data.l[4] = data_types_count > 2 ? data_types[2] : None;
347
348 ds_send_event_to_target(&enter);
349}
350
351static void
352motif_send_enter(Display* dpy, Time time) {
353 XClientMessageEvent enter;
354
355 enter.display = dpy;
356 enter.type = ClientMessage;
357 enter.window = target_window;
358 enter.format = 8;
359 enter.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
360
361 {
362 void* p = &enter.data.b[0];
363 int flags = 0;
364
365 flags |= java_to_motif_actions(source_action) << MOTIF_DND_ACTION_SHIFT;
366 flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
367
368 write_card8(&p, TOP_LEVEL_ENTER | MOTIF_MESSAGE_FROM_INITIATOR);
369 write_card8(&p, MOTIF_BYTE_ORDER);
370 write_card16(&p, flags);
371 write_card32(&p, time);
372 write_card32(&p, awt_dnd_ds_get_source_window());
373 write_card32(&p, _XA_MOTIF_ATOM_0);
374 }
375
376 ds_send_event_to_target(&enter);
377}
378
379static void
380send_enter(Display* dpy, Time time) {
381 switch (target_protocol) {
382 case XDND_PROTOCOL:
383 xdnd_send_enter(dpy, time);
384 break;
385 case MOTIF_DND_PROTOCOL:
386 motif_send_enter(dpy, time);
387 break;
388 case NO_PROTOCOL:
389 default:
390 DTRACE_PRINTLN2("%s:%d send_enter: unknown DnD protocol.", __FILE__, __LINE__);
391 break;
392 }
393}
394
395static void
396xdnd_send_move(XMotionEvent* event) {
397 XClientMessageEvent move;
398
399 move.display = event->display;
400 move.type = ClientMessage;
401 move.window = target_window;
402 move.format = 32;
403 move.message_type = XA_XdndPosition;
404 move.data.l[0] = awt_dnd_ds_get_source_window();
405 move.data.l[1] = 0; /* flags */
406 move.data.l[2] = event->x_root << 16 | event->y_root;
407 move.data.l[3] = event->time;
408 move.data.l[4] = java_to_xdnd_action(source_action);
409
410 ds_send_event_to_target(&move);
411}
412
413static void
414motif_send_move(XMotionEvent* event) {
415 XClientMessageEvent move;
416
417 move.display = event->display;
418 move.type = ClientMessage;
419 move.window = target_window;
420 move.format = 8;
421 move.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
422
423 {
424 void* p = move.data.b;
425 int flags = 0;
426
427 flags |= java_to_motif_actions(source_action) << MOTIF_DND_ACTION_SHIFT;
428 flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
429
430 write_card8(&p, DRAG_MOTION | MOTIF_MESSAGE_FROM_INITIATOR);
431 write_card8(&p, MOTIF_BYTE_ORDER);
432 write_card16(&p, flags);
433 write_card32(&p, event->time);
434 write_card16(&p, event->x_root);
435 write_card16(&p, event->y_root);
436 }
437
438 ds_send_event_to_target(&move);
439}
440
441static void
442send_move(XMotionEvent* event) {
443 switch (target_protocol) {
444 case XDND_PROTOCOL:
445 xdnd_send_move(event);
446 break;
447 case MOTIF_DND_PROTOCOL:
448 motif_send_move(event);
449 break;
450 case NO_PROTOCOL:
451 default:
452 DTRACE_PRINTLN2("%s:%d send_move: unknown DnD protocol.", __FILE__, __LINE__);
453 break;
454 }
455}
456
457static void
458xdnd_send_leave(Display* dpy, Time time) {
459 XClientMessageEvent leave;
460
461 leave.display = dpy;
462 leave.type = ClientMessage;
463 leave.window = target_window;
464 leave.format = 32;
465 leave.message_type = XA_XdndLeave;
466 leave.data.l[0] = awt_dnd_ds_get_source_window();
467 leave.data.l[1] = 0;
468 leave.data.l[2] = 0;
469 leave.data.l[3] = 0;
470 leave.data.l[4] = 0;
471
472 ds_send_event_to_target(&leave);
473}
474
475static void
476motif_send_leave(Display* dpy, Time time) {
477 XClientMessageEvent leave;
478
479 leave.display = dpy;
480 leave.type = ClientMessage;
481 leave.window = target_window;
482 leave.format = 8;
483 leave.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
484
485 {
486 void* p = &leave.data.b[0];
487
488 write_card8(&p, TOP_LEVEL_LEAVE | MOTIF_MESSAGE_FROM_INITIATOR);
489 write_card8(&p, MOTIF_BYTE_ORDER);
490 write_card16(&p, 0);
491 write_card32(&p, time);
492 write_card32(&p, awt_dnd_ds_get_source_window());
493 }
494
495 ds_send_event_to_target(&leave);
496}
497
498static void
499send_leave(Display* dpy, Time time) {
500 switch (target_protocol) {
501 case XDND_PROTOCOL:
502 xdnd_send_leave(dpy, time);
503 break;
504 case MOTIF_DND_PROTOCOL:
505 motif_send_leave(dpy, time);
506 break;
507 case NO_PROTOCOL:
508 default:
509 DTRACE_PRINTLN2("%s:%d send_leave: unknown DnD protocol.", __FILE__, __LINE__);
510 break;
511 }
512}
513
514
515static void
516xdnd_send_drop(XButtonEvent* event) {
517 XClientMessageEvent drop;
518
519 drop.display = event->display;
520 drop.type = ClientMessage;
521 drop.window = target_window;
522 drop.format = 32;
523 drop.message_type = XA_XdndDrop;
524 drop.data.l[0] = awt_dnd_ds_get_source_window();
525 drop.data.l[1] = 0; /* flags */
526 drop.data.l[2] = event->time; /* ### */
527 drop.data.l[3] = 0;
528 drop.data.l[4] = 0;
529
530 ds_send_event_to_target(&drop);
531}
532
533static void
534motif_send_drop(XButtonEvent* event) {
535 XClientMessageEvent drop;
536
537 /*
538 * Motif drop sites expect TOP_LEVEL_LEAVE before DROP_START.
539 */
540 motif_send_leave(event->display, event->time);
541
542 drop.display = event->display;
543 drop.type = ClientMessage;
544 drop.window = target_window;
545 drop.format = 8;
546 drop.message_type = _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
547
548 {
549 void* p = &drop.data.b[0];
550 int flags = 0;
551
552 flags |= java_to_motif_actions(source_action) << MOTIF_DND_ACTION_SHIFT;
553 flags |= java_to_motif_actions(source_actions) << MOTIF_DND_ACTIONS_SHIFT;
554
555 write_card8(&p, DROP_START | MOTIF_MESSAGE_FROM_INITIATOR);
556 write_card8(&p, MOTIF_BYTE_ORDER);
557 write_card16(&p, flags);
558 write_card32(&p, event->time);
559 write_card16(&p, event->x_root);
560 write_card16(&p, event->y_root);
561 write_card32(&p, _XA_MOTIF_ATOM_0);
562 write_card32(&p, awt_dnd_ds_get_source_window());
563 }
564
565 ds_send_event_to_target(&drop);
566}
567
568static void
569send_drop(XButtonEvent* event) {
570 switch (target_protocol) {
571 case XDND_PROTOCOL:
572 xdnd_send_drop(event);
573 break;
574 case MOTIF_DND_PROTOCOL:
575 motif_send_drop(event);
576 break;
577 case NO_PROTOCOL:
578 default:
579 DTRACE_PRINTLN2("%s:%d send_drop: unknown DnD protocol.", __FILE__, __LINE__);
580 break;
581 }
582}
583
584static void
585remove_dnd_grab(Display* dpy, Time time) {
586 XUngrabPointer(dpy, time);
587 XUngrabKeyboard(dpy, time);
588
589 /* Restore the root event mask if it was changed. */
590 if ((your_root_event_mask | ROOT_EVENT_MASK) != your_root_event_mask &&
591 drag_root_window != None) {
592
593 XSelectInput(dpy, drag_root_window, your_root_event_mask);
594
595 drag_root_window = None;
596 your_root_event_mask = NoEventMask;
597 }
598}
599
600static void
601cleanup_target_info(Display* dpy) {
602 target_root_subwindow = None;
603
604 target_window = None;
605 target_proxy_window = None;
606 target_protocol = NO_PROTOCOL;
607 target_protocol_version = 0;
608 target_enter_server_time = CurrentTime;
609 target_action = java_awt_dnd_DnDConstants_ACTION_NONE;
610}
611
612static void
613cleanup_drag(Display* dpy, Time time) {
614 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
615
616 if (dnd_in_progress) {
617 if (target_window != None) {
618 send_leave(dpy, time);
619 }
620
621 if (target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
622 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
623 ds_postDragSourceEvent(env, x_root, y_root);
624 }
625
626 ds_postDragSourceDropEvent(env, JNI_FALSE,
627 java_awt_dnd_DnDConstants_ACTION_NONE,
628 x_root, y_root);
629 }
630
631 /* Cleanup the global state */
632 dnd_in_progress = False;
633 drag_in_progress = False;
634 data_types_count = 0;
635 if (data_types != NULL) {
636 free(data_types);
637 data_types = NULL;
638 }
639 if (!JNU_IsNull(env, source_peer)) {
640 (*env)->DeleteGlobalRef(env, source_peer);
641 source_peer = NULL;
642 }
643
644 cleanup_target_info(dpy);
645
646 remove_dnd_grab(dpy, time);
647
648 XDeleteProperty(awt_display, awt_dnd_ds_get_source_window(), _XA_MOTIF_ATOM_0);
649 XDeleteProperty(awt_display, awt_dnd_ds_get_source_window(), XA_XdndTypeList);
650 XDeleteProperty(awt_display, awt_dnd_ds_get_source_window(), XA_XdndActionList);
651 XtDisownSelection(awt_root_shell, _XA_MOTIF_ATOM_0, time);
652 XtDisownSelection(awt_root_shell, XA_XdndSelection, time);
653
654 awt_cleanupConvertDataContext(env, _XA_MOTIF_ATOM_0);
655 awt_cleanupConvertDataContext(env, XA_XdndSelection);
656}
657
658static void
659process_drop(XButtonEvent* event) {
660 unsigned char ret;
661 XWindowAttributes xwa;
662
663 DASSERT(target_window != None);
664
665 XGetWindowAttributes(event->display, target_window, &xwa);
666
667 target_window_mask = xwa.your_event_mask;
668
669 /* Select for DestoyNotify to cleanup if the target crashes. */
670 ret = checked_XSelectInput(event->display, target_window,
671 (target_window_mask | StructureNotifyMask));
672
673 if (ret == Success) {
674 send_drop(event);
675 } else {
676 DTRACE_PRINTLN2("%s:%d drop rejected - invalid window.",
677 __FILE__, __LINE__);
678 cleanup_drag(event->display, event->time);
679 }
680}
681
682static Window
683find_client_window(Display* dpy, Window window) {
684 Window root, parent, *children;
685 unsigned int nchildren, idx;
686
687 Atom type;
688 int format;
689 unsigned long nitems;
690 unsigned long after;
691 unsigned char *data;
692 Status ret;
693
694 if (XGetWindowProperty(dpy, window, XA_WM_STATE, 0, 0, False,
695 AnyPropertyType, &type, &format, &nitems,
696 &after, &data) == Success) {
697 XFree(data);
698 }
699
700 if (type != None) {
701 return window;
702 }
703
704 if (!XQueryTree(dpy, window, &root, &parent, &children, &nchildren)) {
705 return None;
706 }
707
708 if (children == NULL) {
709 return None;
710 }
711
712 for (idx = 0; idx < nchildren; idx++) {
713 Window win = find_client_window(dpy, children[idx]);
714 if (win != None) {
715 XFree(children);
716 return win;
717 }
718 }
719
720 XFree(children);
721 return None;
722}
723
724static void
725do_update_target_window(Display* dpy, Window subwindow, Time time) {
726 Window client_window = None;
727 Window proxy_window = None;
728 Protocol protocol = NO_PROTOCOL;
729 unsigned int protocol_version = 0;
730 Boolean is_receiver = False;
731
732 client_window = find_client_window(dpy, subwindow);
733
734 if (client_window != None) {
735 /* Request status */
736 int status;
737
738 /* Returns of XGetWindowProperty */
739 Atom type;
740 int format;
741 unsigned long nitems;
742 unsigned long after;
743 unsigned char *data;
744
745 /*
746 * No need for checked_XGetWindowProperty, since we check the returned
747 * property type anyway.
748 */
749 if (drag_source_policy != DS_POLICY_ONLY_XDND) {
750
751 data = NULL;
752 status = XGetWindowProperty(dpy, client_window,
753 _XA_MOTIF_DRAG_RECEIVER_INFO,
754 0, 0xFFFF, False, AnyPropertyType,
755 &type, &format, &nitems, &after, &data);
756
757 if (status == Success && data != NULL && type != None && format == 8
758 && nitems >= MOTIF_RECEIVER_INFO_SIZE) {
759 unsigned char byte_order = read_card8((char*)data, 0);
760 unsigned char drag_protocol_style = read_card8((char*)data, 2);
761
762 switch (drag_protocol_style) {
763 case MOTIF_PREFER_PREREGISTER_STYLE :
764 case MOTIF_PREFER_DYNAMIC_STYLE :
765 case MOTIF_DYNAMIC_STYLE :
766 case MOTIF_PREFER_RECEIVER_STYLE :
767 proxy_window = read_card32((char*)data, 4, byte_order);
768 protocol = MOTIF_DND_PROTOCOL;
769 protocol_version = read_card8((char*)data, 1);
770 is_receiver = True;
771 break;
772 default:
773 DTRACE_PRINTLN3("%s:%d unsupported protocol style (%d).",
774 __FILE__, __LINE__, (int)drag_protocol_style);
775 }
776 }
777
778 if (status == Success) {
779 XFree(data);
780 data = NULL;
781 }
782 }
783
784 if (drag_source_policy != DS_POLICY_ONLY_MOTIF &&
785 (drag_source_policy != DS_POLICY_PREFER_MOTIF || !is_receiver)) {
786
787 data = NULL;
788 status = XGetWindowProperty(dpy, client_window, XA_XdndAware, 0, 1,
789 False, AnyPropertyType, &type, &format,
790 &nitems, &after, &data);
791
792 if (status == Success && data != NULL && type == XA_ATOM) {
793 unsigned int target_version = *((unsigned int*)data);
794
795 if (target_version >= XDND_MIN_PROTOCOL_VERSION) {
796 proxy_window = None;
797 protocol = XDND_PROTOCOL;
798 protocol_version = target_version < XDND_PROTOCOL_VERSION ?
799 target_version : XDND_PROTOCOL_VERSION;
800 is_receiver = True;
801 }
802 }
803
804 /* Retrieve the proxy window handle and check if it is valid. */
805 if (protocol == XDND_PROTOCOL) {
806 if (status == Success) {
807 XFree(data);
808 }
809
810 data = NULL;
811 status = XGetWindowProperty(dpy, client_window, XA_XdndProxy, 0,
812 1, False, XA_WINDOW, &type, &format,
813 &nitems, &after, &data);
814
815 if (status == Success && data != NULL && type == XA_WINDOW) {
816 proxy_window = *((Window*)data);
817 }
818
819 if (proxy_window != None) {
820 if (status == Success) {
821 XFree(data);
822 }
823
824 data = NULL;
825 status = XGetWindowProperty(dpy, proxy_window, XA_XdndProxy,
826 0, 1, False, XA_WINDOW, &type,
827 &format, &nitems, &after, &data);
828
829 if (status != Success || data == NULL || type != XA_WINDOW ||
830 *((Window*)data) != proxy_window) {
831 proxy_window = None;
832 } else {
833 if (status == Success) {
834 XFree(data);
835 }
836
837 data = NULL;
838 status = XGetWindowProperty(dpy, proxy_window,
839 XA_XdndAware, 0, 1, False,
840 AnyPropertyType, &type,
841 &format, &nitems, &after,
842 &data);
843
844 if (status != Success || data == NULL || type != XA_ATOM) {
845 proxy_window = None;
846 }
847 }
848 }
849 }
850
851 XFree(data);
852 }
853
854 if (proxy_window == None) {
855 proxy_window = client_window;
856 }
857 }
858
859 if (is_receiver) {
860 target_window = client_window;
861 target_proxy_window = proxy_window;
862 target_protocol = protocol;
863 target_protocol_version = protocol_version;
864 } else {
865 target_window = None;
866 target_proxy_window = None;
867 target_protocol = NO_PROTOCOL;
868 target_protocol_version = 0;
869 }
870
871 target_action = java_awt_dnd_DnDConstants_ACTION_NONE;
872
873 if (target_window != None) {
874 target_enter_server_time = time;
875 } else {
876 target_enter_server_time = CurrentTime;
877 }
878
879 target_root_subwindow = subwindow;
880}
881
882static void
883update_target_window(XMotionEvent* event) {
884 Display* dpy = event->display;
885 int x = event->x_root;
886 int y = event->x_root;
887 Time time = event->time;
888 Window subwindow = event->subwindow;
889
890 /*
891 * If this event had occurred before the pointer was grabbed,
892 * query the server for the current root subwindow.
893 */
894 if (event->window != event->root) {
895 int xw, yw, xr, yr;
896 unsigned int modifiers;
897 XQueryPointer(dpy, event->root, &event->root, &subwindow,
898 &xr, &yr, &xw, &yw, &modifiers);
899 }
900
901 if (target_root_subwindow != subwindow) {
902 if (target_window != None) {
903 send_leave(dpy, time);
904
905 /*
906 * Neither Motif DnD nor XDnD provide a mean for the target
907 * to notify the source that the pointer exits the drop site
908 * that occupies the whole top level.
909 * We detect this situation and post dragExit.
910 */
911 if (target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
912 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
913 ds_postDragSourceEvent(env, x, y);
914 }
915 }
916
917 /* Update the global state. */
918 do_update_target_window(dpy, subwindow, time);
919
920 if (target_window != None) {
921 send_enter(dpy, time);
922 }
923 }
924}
925
926/*
927 * Updates the source action based on the specified event state.
928 * Returns True if source action changed, False otherwise.
929 */
930static Boolean
931update_source_action(unsigned int state) {
932 JNIEnv* env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
933 jint action = ds_convertModifiersToDropAction(env, state);
934 if (source_action == action) {
935 return False;
936 }
937 source_action = action;
938 return True;
939}
940
941static void
942handle_mouse_move(XMotionEvent* event) {
943 if (!drag_in_progress) {
944 return;
945 }
946
947 if (x_root != event->x_root || y_root != event->y_root) {
948 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
949 ds_postDragSourceDragEvent(env, target_action, event->state,
950 event->x_root, event->y_root,
951 sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_MOUSE_MOVED);
952
953 x_root = event->x_root;
954 y_root = event->y_root;
955 }
956
957 if (event_state != event->state) {
958 if (update_source_action(event->state) && target_window != None) {
959 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
960 ds_postDragSourceDragEvent(env, target_action, event->state,
961 event->x_root, event->y_root,
962 sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_CHANGED);
963 }
964 event_state = event->state;
965 }
966
967 update_target_window(event);
968
969 if (target_window != None) {
970 send_move(event);
971 }
972}
973
974static Boolean
975handle_xdnd_status(XClientMessageEvent* event) {
976 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
977 long* event_data = event->data.l;
978 Window target_win = None;
979 jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
980
981 DTRACE_PRINTLN4("%s:%d XdndStatus target_window=%ld target_protocol=%d.",
982 __FILE__, __LINE__, target_window, target_protocol);
983
984 if (target_protocol != XDND_PROTOCOL) {
985 DTRACE_PRINTLN2("%s:%d XdndStatus rejected - invalid state.",
986 __FILE__, __LINE__);
987 return True;
988 }
989
990 target_win = event_data[0];
991
992 /* Ignore XDnD messages from all other windows. */
993 if (target_window != target_win) {
994 DTRACE_PRINTLN4("%s:%d XdndStatus rejected - invalid target window cur=%ld this=%ld.",
995 __FILE__, __LINE__, target_window, target_win);
996 return True;
997 }
998
999 if (event_data[1] & XDND_ACCEPT_DROP_FLAG) {
1000 /* This feature is new in XDnD version 2, but we can use it as XDnD
1001 compliance only requires supporting version 3 and up. */
1002 action = xdnd_to_java_action(event_data[4]);
1003 }
1004
1005 if (action == java_awt_dnd_DnDConstants_ACTION_NONE &&
1006 target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
1007 ds_postDragSourceEvent(env, x_root, y_root);
1008 } else if (action != java_awt_dnd_DnDConstants_ACTION_NONE) {
1009 jint type = 0;
1010
1011 if (target_action == java_awt_dnd_DnDConstants_ACTION_NONE) {
1012 type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_ENTER;
1013 } else {
1014 type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_MOTION;
1015 }
1016
1017 ds_postDragSourceDragEvent(env, action, event_state,
1018 x_root, y_root, type);
1019 }
1020
1021 target_action = action;
1022
1023 return True;
1024}
1025
1026static Boolean
1027handle_xdnd_finished(XClientMessageEvent* event) {
1028 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1029 long* event_data = event->data.l;
1030 Window target_win = None;
1031 jboolean success = JNI_TRUE;
1032 jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
1033
1034 if (target_protocol != XDND_PROTOCOL) {
1035 DTRACE_PRINTLN2("%s:%d XdndStatus rejected - invalid state.",
1036 __FILE__, __LINE__);
1037 return True;
1038 }
1039
1040 target_win = event_data[0];
1041
1042 /* Ignore XDnD messages from all other windows. */
1043 if (target_window != target_win) {
1044 DTRACE_PRINTLN4("%s:%d XdndStatus rejected - invalid target window cur=%ld this=%ld.",
1045 __FILE__, __LINE__, target_window, target_win);
1046 return True;
1047 }
1048
1049 if (target_protocol_version >= 5) {
1050 success = (event_data[1] & XDND_ACCEPT_DROP_FLAG) != 0 ?
1051 JNI_TRUE : JNI_FALSE;
1052 action = xdnd_to_java_action(event_data[2]);
1053 } else {
1054 /* Assume that the drop was successful and the performed drop action is
1055 the drop action accepted with the latest XdndStatus message. */
1056 success = JNI_TRUE;
1057 action = target_action;
1058 }
1059
1060 ds_postDragSourceDropEvent(env, success, action, x_root, y_root);
1061
1062 dnd_in_progress = False;
1063
1064 XSelectInput(event->display, target_win, target_window_mask);
1065
1066 cleanup_drag(event->display, CurrentTime);
1067
1068 return True;
1069}
1070
1071static Boolean
1072handle_motif_client_message(XClientMessageEvent* event) {
1073 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1074 int reason = (int)(event->data.b[0] & MOTIF_MESSAGE_REASON_MASK);
1075 int origin = (int)(event->data.b[0] & MOTIF_MESSAGE_SENDER_MASK);
1076 unsigned char byte_order = event->data.b[1];
1077 jint action = java_awt_dnd_DnDConstants_ACTION_NONE;
1078 Time time = CurrentTime;
1079 int x = 0, y = 0;
1080
1081 /* Only receiver messages should be handled. */
1082 if (origin != MOTIF_MESSAGE_FROM_RECEIVER) {
1083 return False;
1084 }
1085
1086 if (target_protocol != MOTIF_DND_PROTOCOL) {
1087 DTRACE_PRINTLN2("%s:%d _MOTIF_DRAG_AND_DROP_MESSAGE rejected - invalid state.",
1088 __FILE__, __LINE__);
1089 return True;
1090 }
1091
1092 switch (reason) {
1093 case DROP_SITE_ENTER:
1094 case DROP_SITE_LEAVE:
1095 case DRAG_MOTION:
1096 case OPERATION_CHANGED:
1097 break;
1098 default:
1099 return False;
1100 }
1101
1102 time = read_card32(event->data.b, 4, byte_order);
1103
1104 /* Discard events from the previous receiver. */
1105 if (target_enter_server_time == CurrentTime ||
1106 time < target_enter_server_time) {
1107 DTRACE_PRINTLN2("%s:%d _MOTIF_DRAG_AND_DROP_MESSAGE rejected - invalid time.",
1108 __FILE__, __LINE__);
1109 return True;
1110 }
1111
1112 if (reason != DROP_SITE_LEAVE) {
1113 CARD16 flags = read_card16(event->data.b, 2, byte_order);
1114 unsigned char status = (flags & MOTIF_DND_STATUS_MASK) >>
1115 MOTIF_DND_STATUS_SHIFT;
1116 unsigned char motif_action = (flags & MOTIF_DND_ACTION_MASK) >>
1117 MOTIF_DND_ACTION_SHIFT;
1118
1119 if (status == MOTIF_VALID_DROP_SITE) {
1120 action = motif_to_java_actions(motif_action);
1121 } else {
1122 action = java_awt_dnd_DnDConstants_ACTION_NONE;
1123 }
1124
1125 x = read_card16(event->data.b, 8, byte_order);
1126 y = read_card16(event->data.b, 10, byte_order);
1127 }
1128
1129 /*
1130 * We should derive the type of java event to post not from the message
1131 * reason, but from the combination of the current and previous target
1132 * actions:
1133 * Even if the reason is DROP_SITE_LEAVE we shouldn't post dragExit
1134 * if the drag was rejected earlier.
1135 * Even if the reason is DROP_SITE_ENTER we shouldn't post dragEnter
1136 * if the drag is not accepted.
1137 */
1138 if (target_action != java_awt_dnd_DnDConstants_ACTION_NONE &&
1139 action == java_awt_dnd_DnDConstants_ACTION_NONE) {
1140
1141 ds_postDragSourceEvent(env, x, y);
1142 } else if (action != java_awt_dnd_DnDConstants_ACTION_NONE) {
1143 jint type = 0;
1144
1145 if (target_action == java_awt_dnd_DnDConstants_ACTION_NONE) {
1146 type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_ENTER;
1147 } else {
1148 type = sun_awt_dnd_SunDragSourceContextPeer_DISPATCH_MOTION;
1149 }
1150
1151 ds_postDragSourceDragEvent(env, action, event_state, x, y, type);
1152 }
1153
1154 target_action = action;
1155
1156 return True;
1157}
1158
1159/*
1160 * Handles client messages.
1161 * Returns True if the event is processed, False otherwise.
1162 */
1163static Boolean
1164handle_client_message(XClientMessageEvent* event) {
1165 if (event->message_type == XA_XdndStatus) {
1166 return handle_xdnd_status(event);
1167 } else if (event->message_type == XA_XdndFinished) {
1168 return handle_xdnd_finished(event);
1169 } else if (event->message_type == _XA_MOTIF_DRAG_AND_DROP_MESSAGE) {
1170 return handle_motif_client_message(event);
1171 }
1172 return False;
1173}
1174
1175/*
1176 * Similar to XtLastTimestampProcessed(). We cannot use Xt time stamp, as it is
1177 * updated in XtDispatchEvent that may not be called if a java event is
1178 * consumed. This can make Xt time stamp out-of-date and cause XGrab* failures
1179 * with GrabInvalidTime reason.
1180 */
1181static Time
1182get_latest_time_stamp() {
1183 return latest_time_stamp;
1184}
1185
1186static void
1187update_latest_time_stamp(XEvent* event) {
1188 Time time = latest_time_stamp;
1189
1190 switch (event->type) {
1191 case KeyPress:
1192 case KeyRelease: time = event->xkey.time; break;
1193 case ButtonPress:
1194 case ButtonRelease: time = event->xbutton.time; break;
1195 case MotionNotify: time = event->xmotion.time; break;
1196 case EnterNotify:
1197 case LeaveNotify: time = event->xcrossing.time; break;
1198 case PropertyNotify: time = event->xproperty.time; break;
1199 case SelectionClear: time = event->xselectionclear.time; break;
1200 }
1201
1202 latest_time_stamp = time;
1203}
1204
1205Boolean
1206awt_dnd_ds_process_event(XEvent* event) {
1207 Display* dpy = event->xany.display;
1208
1209 update_latest_time_stamp(event);
1210
1211 if (process_proxy_mode_event(event)) {
1212 return True;
1213 }
1214
1215 if (!dnd_in_progress) {
1216 return False;
1217 }
1218
1219 /* Process drag and drop messages. */
1220 switch (event->type) {
1221 case ClientMessage:
1222 return handle_client_message(&event->xclient);
1223 case DestroyNotify:
1224 /* Target crashed during drop processing - cleanup. */
1225 if (!drag_in_progress &&
1226 event->xdestroywindow.window == target_window) {
1227 cleanup_drag(dpy, CurrentTime);
1228 return True;
1229 }
1230 /* Pass along */
1231 return False;
1232 }
1233
1234 if (!drag_in_progress) {
1235 return False;
1236 }
1237
1238 /* Process drag-only messages. */
1239 switch (event->type) {
1240 case KeyRelease:
1241 case KeyPress: {
1242 KeySym keysym = XKeycodeToKeysym(dpy, event->xkey.keycode, 0);
1243 switch (keysym) {
1244 case XK_Escape: {
1245 if (keysym == XK_Escape) {
1246 remove_dnd_grab(dpy, event->xkey.time);
1247 cleanup_drag(dpy, event->xkey.time);
1248 }
1249 break;
1250 }
1251 case XK_Control_R:
1252 case XK_Control_L:
1253 case XK_Shift_R:
1254 case XK_Shift_L: {
1255 Window subwindow;
1256 int xw, yw, xr, yr;
1257 unsigned int modifiers;
1258 XQueryPointer(event->xkey.display, event->xkey.root, &event->xkey.root, &subwindow,
1259 &xr, &yr, &xw, &yw, &modifiers);
1260 event->xkey.state = modifiers;
1261 //It's safe to use key event as motion event since we use only their common fields.
1262 handle_mouse_move(&event->xmotion);
1263 break;
1264 }
1265 }
1266 return True;
1267 }
1268 case ButtonPress:
1269 return True;
1270 case MotionNotify:
1271 handle_mouse_move(&event->xmotion);
1272 return True;
1273 case ButtonRelease:
1274 /*
1275 * On some X servers it could happen that ButtonRelease coordinates
1276 * differ from the latest MotionNotify coordinates, so we need to
1277 * process it as a mouse motion.
1278 * MotionNotify differs from ButtonRelease only in is_hint member, but
1279 * we never use it, so it is safe to cast to MotionNotify.
1280 */
1281 handle_mouse_move(&event->xmotion);
1282 if (event->xbutton.button == Button1 || event->xbutton.button == Button2) {
1283 // drag is initiated with Button1 or Button2 pressed and
1284 // ended on release of either of these buttons (as the same
1285 // behavior was with our old Motif DnD-based implementation)
1286 remove_dnd_grab(dpy, event->xbutton.time);
1287 drag_in_progress = False;
1288 if (target_window != None && target_action != java_awt_dnd_DnDConstants_ACTION_NONE) {
1289 /*
1290 * ACTION_NONE indicates that either the drop target rejects the
1291 * drop or it haven't responded yet. The latter could happen in
1292 * case of fast drag, slow target-server connection or slow
1293 * drag notifications processing on the target side.
1294 */
1295 process_drop(&event->xbutton);
1296 } else {
1297 cleanup_drag(dpy, event->xbutton.time);
1298 }
1299 }
1300 return True;
1301 default:
1302 return False;
1303 }
1304}
1305
1306static Boolean
1307motif_convert_proc(Widget w, Atom* selection, Atom* target, Atom* type,
1308 XtPointer* value, unsigned long* length, int32_t* format) {
1309
1310 if (*target == XA_XmTRANSFER_SUCCESS ||
1311 *target == XA_XmTRANSFER_FAILURE) {
1312
1313 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1314 jboolean success =
1315 (*target == XA_XmTRANSFER_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1316
1317 ds_postDragSourceDropEvent(env, success, target_action,
1318 x_root, y_root);
1319
1320 dnd_in_progress = False;
1321
1322 XSelectInput(XtDisplay(w), target_window, target_window_mask);
1323
1324 cleanup_drag(XtDisplay(w), CurrentTime);
1325
1326 *type = *target;
1327 *length = 0;
1328 *format = 32;
1329 *value = NULL;
1330
1331 return True;
1332 } else {
1333 return awt_convertData(w, selection, target, type, value, length,
1334 format);
1335 }
1336}
1337
1338static Boolean
1339set_convert_data_context(JNIEnv* env, Display* dpy, XID xid, jobject component,
1340 jobject transferable, jobject formatMap,
1341 jlongArray formats) {
1342 awt_convertDataCallbackStruct* structPtr = NULL;
1343
1344 if (XFindContext(awt_display, xid, awt_convertDataContext,
1345 (XPointer*)&structPtr) == XCNOMEM || structPtr != NULL) {
1346 return False;
1347 }
1348
1349 structPtr = calloc(1, sizeof(awt_convertDataCallbackStruct));
1350 if (structPtr == NULL) {
1351 return False;
1352 }
1353
1354 structPtr->source = (*env)->NewGlobalRef(env, component);
1355 structPtr->transferable = (*env)->NewGlobalRef(env, transferable);
1356 structPtr->formatMap = (*env)->NewGlobalRef(env, formatMap);
1357 structPtr->formats = (*env)->NewGlobalRef(env, formats);
1358
1359 if (JNU_IsNull(env, structPtr->source) ||
1360 JNU_IsNull(env, structPtr->transferable) ||
1361 JNU_IsNull(env, structPtr->formatMap) ||
1362 JNU_IsNull(env, structPtr->formats)) {
1363
1364 if (!JNU_IsNull(env, structPtr->source)) {
1365 (*env)->DeleteGlobalRef(env, structPtr->source);
1366 }
1367 if (!JNU_IsNull(env, structPtr->transferable)) {
1368 (*env)->DeleteGlobalRef(env, structPtr->transferable);
1369 }
1370 if (!JNU_IsNull(env, structPtr->formatMap)) {
1371 (*env)->DeleteGlobalRef(env, structPtr->formatMap);
1372 }
1373 if (!JNU_IsNull(env, structPtr->formats)) {
1374 (*env)->DeleteGlobalRef(env, structPtr->formats);
1375 }
1376 free(structPtr);
1377 return False;
1378 }
1379
1380 if (XSaveContext(dpy, xid, awt_convertDataContext,
1381 (XPointer)structPtr) == XCNOMEM) {
1382 free(structPtr);
1383 return False;
1384 }
1385
1386 return True;
1387}
1388
1389/*
1390 * Convenience routine. Constructs an appropriate exception message based on the
1391 * specified prefix and the return code of XGrab* function and throws an
1392 * InvalidDnDOperationException with the constructed message.
1393 */
1394static void
1395throw_grab_failure_exception(JNIEnv* env, int ret_code, char* msg_prefix) {
1396 char msg[200];
1397 char* msg_cause = "";
1398
1399 switch (ret_code) {
1400 case GrabNotViewable: msg_cause = "not viewable"; break;
1401 case AlreadyGrabbed: msg_cause = "already grabbed"; break;
1402 case GrabInvalidTime: msg_cause = "invalid time"; break;
1403 case GrabFrozen: msg_cause = "grab frozen"; break;
1404 default: msg_cause = "unknown failure"; break;
1405 }
1406
1407 sprintf(msg, "%s: %s.", msg_prefix, msg_cause);
1408 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1409 msg);
1410}
1411
1412/*
1413 * Sets the proxy mode source window - the source window which the drag
1414 * notifications from an XEmbed client should be forwarded to.
1415 * If the window is not None and there is a drag operation in progress,
1416 * throws InvalidDnDOperationException and doesn't change
1417 * proxy_mode_source_window.
1418 * The caller mush hold AWT_LOCK.
1419 */
1420void
1421set_proxy_mode_source_window(Window window) {
1422 if (window != None && dnd_in_progress) {
1423 JNIEnv *env = (JNIEnv*)JNU_GetEnv(jvm, JNI_VERSION_1_4);
1424 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1425 "Drag and drop is already in progress.");
1426 return;
1427 }
1428
1429 proxy_mode_source_window = window;
1430}
1431
1432/*
1433 * Checks if the event is a drag notification from an XEmbed client.
1434 * If it is, forwards this event back to the current source and returns True.
1435 * Otherwise, returns False.
1436 * Currently only XDnD protocol notifications are recognized.
1437 * The caller must hold AWT_LOCK.
1438 */
1439static Boolean
1440process_proxy_mode_event(XEvent* event) {
1441 if (proxy_mode_source_window == None) {
1442 return False;
1443 }
1444
1445 if (event->type == ClientMessage) {
1446 XClientMessageEvent* xclient = &event->xclient;
1447 if (xclient->message_type == XA_XdndStatus ||
1448 xclient->message_type == XA_XdndFinished) {
1449 Window source = proxy_mode_source_window;
1450
1451 xclient->data.l[0] = xclient->window;
1452 xclient->window = source;
1453
1454 XSendEvent(xclient->display, source, False, NoEventMask,
1455 (XEvent*)xclient);
1456
1457 if (xclient->message_type == XA_XdndFinished) {
1458 proxy_mode_source_window = None;
1459 }
1460
1461 return True;
1462 }
1463 }
1464
1465 return False;
1466}
1467
1468/*
1469 * Class: sun_awt_motif_X11DragSourceContextPeer
1470 * Method: startDrag
1471 * Signature: ()V
1472 */
1473JNIEXPORT void JNICALL
1474Java_sun_awt_motif_X11DragSourceContextPeer_startDrag(JNIEnv *env,
1475 jobject this,
1476 jobject component,
1477 jobject wpeer,
1478 jobject transferable,
1479 jobject trigger,
1480 jobject cursor,
1481 jint ctype,
1482 jint actions,
1483 jlongArray formats,
1484 jobject formatMap) {
1485 Time time_stamp = CurrentTime;
1486 Cursor xcursor = None;
1487 Window root_window = None;
1488 Atom* targets = NULL;
1489 jsize num_targets = 0;
1490
1491 AWT_LOCK();
1492
1493 if (dnd_in_progress) {
1494 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1495 "Drag and drop is already in progress.");
1496 AWT_UNLOCK();
1497 return;
1498 }
1499
1500 if (proxy_mode_source_window != None) {
1501 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1502 "Proxy drag is in progress.");
1503 AWT_UNLOCK();
1504 return;
1505 }
1506
1507 if (!awt_dnd_init(awt_display)) {
1508 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1509 "DnD subsystem initialization failed.");
1510 AWT_UNLOCK();
1511 return;
1512 }
1513
1514 if (!JNU_IsNull(env, cursor)) {
1515 xcursor = getCursor(env, cursor);
1516
1517 if (xcursor == None) {
1518 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1519 "Invalid drag cursor");
1520 AWT_UNLOCK();
1521 }
1522 }
1523
1524 /* Determine the root window for the drag operation. */
1525 {
1526 struct FrameData* wdata = (struct FrameData*)
1527 JNU_GetLongFieldAsPtr(env, wpeer, mComponentPeerIDs.pData);
1528
1529 if (wdata == NULL) {
1530 JNU_ThrowNullPointerException(env, "Null component data");
1531 AWT_UNLOCK();
1532 return;
1533 }
1534
1535 if (wdata->winData.shell == NULL) {
1536 JNU_ThrowNullPointerException(env, "Null shell widget");
1537 AWT_UNLOCK();
1538 return;
1539 }
1540
1541 root_window = RootWindowOfScreen(XtScreen(wdata->winData.shell));
1542
1543 if (root_window == None) {
1544 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1545 "Cannot get the root window for the drag operation.");
1546 AWT_UNLOCK();
1547 return;
1548 }
1549 }
1550
1551 time_stamp = get_latest_time_stamp();
1552
1553 /* Extract the targets from java array. */
1554 {
1555 targets = NULL;
1556 num_targets = (*env)->GetArrayLength(env, formats);
1557
1558 /*
1559 * In debug build GetLongArrayElements aborts with assertion on an empty
1560 * array.
1561 */
1562 if (num_targets > 0) {
1563 jboolean isCopy = JNI_TRUE;
1564 jlong* java_targets = (*env)->GetLongArrayElements(env, formats,
1565 &isCopy);
1566
1567 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
1568 AWT_UNLOCK();
1569 return;
1570 }
1571
1572 if (java_targets != NULL) {
1573 targets = (Atom*)malloc(num_targets * sizeof(Atom));
1574 if (targets != NULL) {
1575#ifdef _LP64
1576 memcpy(targets, java_targets, num_targets * sizeof(Atom));
1577#else
1578 jsize i;
1579
1580 for (i = 0; i < num_targets; i++) {
1581 targets[i] = (Atom)java_targets[i];
1582 }
1583#endif
1584 }
1585 (*env)->ReleaseLongArrayElements(env, formats, java_targets,
1586 JNI_ABORT);
1587 }
1588 }
1589 if (targets == NULL) {
1590 num_targets = 0;
1591 }
1592 }
1593
1594 /* Write the XDnD initiator info on the awt_root_shell. */
1595 {
1596 unsigned char ret;
1597 Atom action_atoms[3];
1598 unsigned int action_count = 0;
1599
1600 if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) {
1601 action_atoms[action_count] = XA_XdndActionCopy;
1602 action_count++;
1603 }
1604 if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) {
1605 action_atoms[action_count] = XA_XdndActionMove;
1606 action_count++;
1607 }
1608 if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) {
1609 action_atoms[action_count] = XA_XdndActionLink;
1610 action_count++;
1611 }
1612
1613 ret = checked_XChangeProperty(awt_display, awt_dnd_ds_get_source_window(),
1614 XA_XdndActionList, XA_ATOM, 32,
1615 PropModeReplace, (unsigned char*)action_atoms,
1616 action_count * sizeof(Atom));
1617
1618 if (ret != Success) {
1619 cleanup_drag(awt_display, time_stamp);
1620 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1621 "Cannot write XdndActionList property");
1622 AWT_UNLOCK();
1623 return;
1624 }
1625
1626 ret = checked_XChangeProperty(awt_display, awt_dnd_ds_get_source_window(),
1627 XA_XdndTypeList, XA_ATOM, 32,
1628 PropModeReplace, (unsigned char*)targets,
1629 num_targets);
1630
1631 if (ret != Success) {
1632 cleanup_drag(awt_display, time_stamp);
1633 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1634 "Cannot write XdndTypeList property");
1635 AWT_UNLOCK();
1636 return;
1637 }
1638 }
1639
1640 /* Write the Motif DnD initiator info on the awt_root_shell. */
1641 {
1642 InitiatorInfo info;
1643 unsigned char ret;
1644 int target_list_index =
1645 get_index_for_target_list(awt_display, targets, num_targets);
1646
1647 if (target_list_index == -1) {
1648 cleanup_drag(awt_display, time_stamp);
1649 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1650 "Cannot determine the target list index.");
1651 AWT_UNLOCK();
1652 return;
1653 }
1654
1655 info.byte_order = MOTIF_BYTE_ORDER;
1656 info.protocol_version = MOTIF_DND_PROTOCOL_VERSION;
1657 info.index = target_list_index;
1658 info.selection_atom = _XA_MOTIF_ATOM_0;
1659
1660 ret = checked_XChangeProperty(awt_display, awt_dnd_ds_get_source_window(),
1661 _XA_MOTIF_ATOM_0,
1662 _XA_MOTIF_DRAG_INITIATOR_INFO, 8,
1663 PropModeReplace, (unsigned char*)&info,
1664 sizeof(InitiatorInfo));
1665
1666 if (ret != Success) {
1667 cleanup_drag(awt_display, time_stamp);
1668 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1669 "Cannot write the Motif DnD initiator info");
1670 AWT_UNLOCK();
1671 return;
1672 }
1673 }
1674
1675 /* Acquire XDnD selection ownership. */
1676 if (XtOwnSelection(awt_root_shell, XA_XdndSelection, time_stamp,
1677 awt_convertData, NULL, NULL) != True) {
1678 cleanup_drag(awt_display, time_stamp);
1679 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1680 "Cannot acquire XdndSelection ownership.");
1681 AWT_UNLOCK();
1682 return;
1683 }
1684
1685 /* Acquire Motif DnD selection ownership. */
1686 if (XtOwnSelection(awt_root_shell, _XA_MOTIF_ATOM_0, time_stamp,
1687 motif_convert_proc, NULL, NULL) != True) {
1688 cleanup_drag(awt_display, time_stamp);
1689 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1690 "Cannot acquire Motif DnD selection ownership.");
1691 AWT_UNLOCK();
1692 return;
1693 }
1694
1695 /*
1696 * Store the information needed to convert data for both selections
1697 * in awt_convertDataContext.
1698 */
1699 {
1700 if (!set_convert_data_context(env, awt_display, XA_XdndSelection,
1701 component, transferable, formatMap,
1702 formats)) {
1703 cleanup_drag(awt_display, time_stamp);
1704 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1705 "Cannot save context for XDnD selection data conversion.");
1706 AWT_UNLOCK();
1707 return;
1708 }
1709
1710 if (!set_convert_data_context(env, awt_display, _XA_MOTIF_ATOM_0,
1711 component, transferable, formatMap,
1712 formats)) {
1713 cleanup_drag(awt_display, time_stamp);
1714 JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
1715 "Cannot save context for Motif DnD selection data conversion.");
1716 AWT_UNLOCK();
1717 return;
1718 }
1719 }
1720
1721 /* Install X grabs. */
1722 {
1723 XWindowAttributes xwa;
1724 int ret;
1725
1726 XGetWindowAttributes(awt_display, root_window, &xwa);
1727
1728 your_root_event_mask = xwa.your_event_mask;
1729
1730 XSelectInput(awt_display, root_window,
1731 your_root_event_mask | ROOT_EVENT_MASK);
1732
1733 ret = XGrabPointer(awt_display,
1734 root_window,
1735 False,
1736 GRAB_EVENT_MASK,
1737 GrabModeAsync,
1738 GrabModeAsync,
1739 None,
1740 xcursor,
1741 time_stamp);
1742
1743 if (ret != GrabSuccess) {
1744 cleanup_drag(awt_display, time_stamp);
1745 throw_grab_failure_exception(env, ret, "Cannot grab pointer");
1746 AWT_UNLOCK();
1747 return;
1748 }
1749
1750 ret = XGrabKeyboard(awt_display,
1751 root_window,
1752 False,
1753 GrabModeAsync,
1754 GrabModeAsync,
1755 time_stamp);
1756
1757 if (ret != GrabSuccess) {
1758 cleanup_drag(awt_display, time_stamp);
1759 throw_grab_failure_exception(env, ret, "Cannot grab keyboard");
1760 AWT_UNLOCK();
1761 return;
1762 }
1763 }
1764
1765 /* Update the global state. */
1766 source_peer = (*env)->NewGlobalRef(env, this);
1767 dnd_in_progress = True;
1768 drag_in_progress = True;
1769 data_types = targets;
1770 data_types_count = num_targets;
1771 source_actions = actions;
1772 drag_root_window = root_window;
1773
1774 AWT_UNLOCK();
1775}
1776
1777/*
1778 * Class: sun_awt_motif_X11DragSourceContextPeer
1779 * Method: setNativeCursor
1780 * Signature: ()V
1781 */
1782JNIEXPORT void JNICALL
1783Java_sun_awt_motif_X11DragSourceContextPeer_setNativeCursor(JNIEnv *env,
1784 jobject this,
1785 jlong nativeCtxt,
1786 jobject cursor,
1787 jint type) {
1788 if (JNU_IsNull(env, cursor)) {
1789 return;
1790 }
1791
1792 XChangeActivePointerGrab(awt_display,
1793 GRAB_EVENT_MASK,
1794 getCursor(env, cursor),
1795 CurrentTime);
1796}