blob: 4944f5ef1c141fb7b78a794c3e9419ac7fe6b51a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2005 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_p.h"
31#include <X11/Intrinsic.h>
32#include <X11/Xutil.h>
33
34#include <sys/utsname.h>
35
36#include <jni.h>
37#include <jni_util.h>
38
39#include "sun_awt_datatransfer_DataTransferer.h"
40#include "sun_awt_motif_MDataTransferer.h"
41
42#include "awt_XmDnD.h"
43#include "awt_DataTransferer.h"
44
45static jclass string;
46
47XContext awt_convertDataContext = 0;
48
49Atom XA_TARGETS;
50
51extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;
52
53typedef enum {
54 SelectionPending,
55 SelectionSuccess,
56 SelectionFailure,
57 SelectionOwnerTimedOut
58} SelectionStatus;
59
60/* Should only be accessed by the current owner of AWT_LOCK. */
61static SelectionStatus globalSelectionStatus = SelectionPending;
62
63static SelectionStatus get_selection_status() {
64 return globalSelectionStatus;
65}
66
67static void set_selection_status(SelectionStatus status) {
68 globalSelectionStatus = status;
69}
70
71static void
72selection_request_filter(Widget widget, XtPointer closure, XEvent *event,
73 Boolean *cont) {
74 if (event->type == SelectionRequest) {
75 Window awt_root_window = XtWindow(awt_root_shell);
76 Atom selection = event->xselectionrequest.selection;
77 Window owner = XGetSelectionOwner(event->xany.display, selection);
78
79 if (owner != awt_root_window) {
80 XSelectionEvent notify;
81
82 notify.type = SelectionNotify;
83 notify.display = event->xselectionrequest.display;
84 notify.requestor = event->xselectionrequest.requestor;
85 notify.selection = event->xselectionrequest.selection;
86 notify.time = event->xselectionrequest.time;
87 notify.target = event->xselectionrequest.target;
88 notify.property = None;
89
90 XSendEvent(notify.display, notify.requestor, False,
91 (unsigned long)0, (XEvent*)&notify);
92 *cont = False;
93 }
94 }
95}
96
97/**
98 * global function to initialize this client as a Dynamic-only app.
99 *
100 * gets called once during toolkit initialization.
101 */
102
103void awt_initialize_DataTransferer() {
104 JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
105 jclass stringClassLocal = NULL;
106
107 DASSERT(string == NULL);
108
109 stringClassLocal = (*env)->FindClass(env, "java/lang/String");
110
111 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
112 (*env)->ExceptionDescribe(env);
113 (*env)->ExceptionClear(env);
114 DASSERT(False);
115 }
116
117 if (JNU_IsNull(env, stringClassLocal)) return;
118
119 string = (*env)->NewGlobalRef(env, stringClassLocal); /* never freed! */
120 (*env)->DeleteLocalRef(env, stringClassLocal);
121
122 if (JNU_IsNull(env, string)) {
123 JNU_ThrowOutOfMemoryError(env, "");
124 return;
125 }
126
127 DASSERT(awt_convertDataContext == 0);
128 awt_convertDataContext = XUniqueContext();
129 DASSERT(awt_convertDataContext != 0);
130
131 /*
132 * Fixes for 4513976 and 4818143.
133 */
134 XtAppSetSelectionTimeout(awt_appContext,
135 JNU_CallStaticMethodByName(env, NULL, "sun/awt/UNIXToolkit",
136 "getDatatransferTimeout", "()I").i);
137
138 /*
139 * Xt selection machinery doesn't respond to SelectionRequests if the
140 * event arrives on a widget that is not the current selection owner.
141 * This can happen if XtDisownSelection was called when SelectionRequest was
142 * already on the native queue.
143 * If the requestor is another JVM, it hangs for the selection timeout
144 * as SelectionNotify is never sent.
145 * We install an event handler that filters out SelectionRequests if the
146 * awt_root_shell is not the current selection owner.
147 */
148 XtAddEventHandler(awt_root_shell, (EventMask)0, True,
149 selection_request_filter, NULL);
150
151 XA_TARGETS = XInternAtom(awt_display, "TARGETS", False);
152}
153
154/*
155 * Single routine to convert to target FILE_NAME or _DT_FILENAME
156 */
157Boolean
158convertFileType(jbyteArray data, Atom * type, XtPointer * value,
159 unsigned long *length, int32_t *format)
160{
161 /*
162 * Convert the internal representation to an File Name.
163 * The data passed is an array of
164 * null separated bytes. Each series of bytes is a string
165 * that is then converted to an XString which are then put
166 * into an XStringList and put into an XTextProperty for
167 * usage in other programs.
168 *
169 * It would be desireable to have dataConvert to this conversion
170 * but it isn't possible to return a byte array that represents
171 * the XTextProperty.
172 */
173
174 JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
175 jboolean isCopy=JNI_FALSE;
176 XTextProperty tp;
177 jsize len;
178 jsize strings = 0;
179 jsize i;
180 char** stringList;
181 Status s;
182 jbyte* bytes;
183 char* start;
184 size_t slen;
185 char* utf;
186
187 if ((*env)->PushLocalFrame(env, 16) < 0) {
188 return False;
189 }
190
191 /* convert the data to an Array of Byte Elements */
192 bytes = (*env)->GetByteArrayElements(env, data, &isCopy);
193 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
194 (*env)->ExceptionDescribe(env);
195 (*env)->ExceptionClear(env);
196 (*env)->PopLocalFrame(env, NULL);
197 return False;
198 }
199
200 if (JNU_IsNull(env, bytes)) {
201 (*env)->PopLocalFrame(env, NULL);
202 return False;
203 }
204
205 /* Get the length of the area */
206 len = (*env)->GetArrayLength(env, data);
207 if (len == 0) {
208 (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);
209 (*env)->PopLocalFrame(env, NULL);
210 return False;
211 }
212
213 /*
214 * determine the number of lists. The byteArray is null separated list of
215 * strings.
216 */
217 for (i = 0; i < len; i++) {
218 if (bytes[i] == '\0') {
219 strings++;
220 }
221 }
222
223 /* Allocate an X11 string list */
224 stringList = (char **)XtCalloc(strings, sizeof(char *));
225 if (stringList == (char**)NULL) {
226 (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);
227 (*env)->PopLocalFrame(env, NULL);
228 return False;
229 }
230
231 for (i = 0; i < strings; i++) {
232 if (i == 0) {
233 start = (char*)bytes;
234 } else {
235 start = (char*)&bytes[slen];
236 }
237
238 /*
239 * if start is a NULL we're at the end of the list
240 * We'll just a have null entry on the end of the list
241 */
242 if (start[0] == '\0') {
243 stringList[i] = NULL;
244 continue;
245 }
246 slen = strlen(start) + 1;
247
248 stringList[i] = (char*)XtCalloc(slen, sizeof(char));
249
250 if (stringList[i] == (char *)NULL) {
251 jsize j;
252
253 (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);
254
255 for (j = 0; j < i; j++) {
256 XtFree((void *)stringList[j]);
257 }
258
259 (*env)->PopLocalFrame(env, NULL);
260
261 return False;
262 }
263
264 memcpy((void *)stringList[i], (const void*)start, slen);
265 }
266
267 (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);
268 s = XStringListToTextProperty(stringList, strings, &tp);
269
270 /* free the strings that were created */
271 for (i = 0; i < strings; i++) {
272 if (stringList[i] != NULL) {
273 XtFree((void*)stringList[i]);
274 }
275 }
276
277 XtFree((void*)stringList);
278
279 if (s == 0) {
280 (*env)->PopLocalFrame(env, NULL);
281 return False;
282 }
283
284 *value = (XtPointer)XtCalloc(tp.nitems, sizeof(char));
285
286 if (*value == (XtPointer)NULL) {
287 XFree((void*)tp.value);
288
289 (*env)->PopLocalFrame(env, NULL);
290
291 return False;
292 }
293
294 memcpy((void *)(*value), (const void *)tp.value, tp.nitems);
295
296 XFree((void*)tp.value);
297
298 *length = tp.nitems;
299 *type = tp.encoding;
300 *format = tp.format;
301 (*env)->PopLocalFrame(env, NULL);
302 return True;
303}
304
305/*
306 * Class: sun_awt_motif_MDataTransferer
307 * Method: getAtomForTarget
308 * Signature: (Ljava/lang/String;)J
309 */
310
311JNIEXPORT jlong JNICALL
312Java_sun_awt_motif_MDataTransferer_getAtomForTarget(JNIEnv *env,
313 jclass cls,
314 jstring targetString)
315{
316 Atom target;
317 char *target_str;
318
319 if (JNU_IsNull(env, targetString)) {
320 JNU_ThrowNullPointerException(env, "NullPointerException");
321 return -1;
322 }
323 target_str = (char *) JNU_GetStringPlatformChars(env, targetString, NULL);
324
325 AWT_LOCK();
326
327 target = XInternAtom(awt_display, target_str, False);
328
329 AWT_UNLOCK();
330
331 JNU_ReleaseStringPlatformChars(env, targetString,
332 (const char *) target_str);
333 return target;
334}
335
336/*
337 * Class: sun_awt_motif_MDataTransferer
338 * Method: getTargetNameForAtom
339 * Signature: (J)Ljava/lang/String;
340 */
341
342JNIEXPORT jstring JNICALL
343Java_sun_awt_motif_MDataTransferer_getTargetNameForAtom(JNIEnv *env,
344 jclass cls,
345 jlong atom)
346{
347 jstring targetString;
348 char *name;
349
350 AWT_LOCK();
351
352 name = XGetAtomName(awt_display, (Atom) atom);
353
354 if (name == NULL) {
355 JNU_ThrowNullPointerException(env, "Failed to retrieve atom name.");
356 AWT_UNLOCK();
357 return NULL;
358 }
359
360 targetString = (*env)->NewStringUTF(env, (const char *)name);
361
362 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
363 (*env)->ExceptionDescribe(env);
364 (*env)->ExceptionClear(env);
365 XFree (name);
366 AWT_UNLOCK();
367 return NULL;
368 }
369
370 if (JNU_IsNull(env, targetString)) {
371 JNU_ThrowNullPointerException(env, "Failed to create a string.");
372 XFree (name);
373 AWT_UNLOCK();
374 return NULL;
375 }
376
377 XFree (name);
378
379 AWT_UNLOCK();
380 return targetString;
381}
382
383/*
384 * Class: sun_awt_datatransfer_DataTransferer
385 * Method: dragQueryFile
386 * Signature: ([B)[Ljava/lang/String;
387 *
388 * This method converts a byte array that came from File most likely from a
389 * drag operation into a String array.
390 */
391
392JNIEXPORT jobjectArray JNICALL
393Java_sun_awt_motif_MDataTransferer_dragQueryFile
394 (JNIEnv *env, jobject this, jbyteArray bytes)
395{
396 XTextProperty tp;
397 jbyte *value;
398
399 char** strings = (char **)NULL;
400 int32_t nstrings = 0;
401 jobject filenames;
402 jobject ret = NULL;
403 int32_t i;
404 jsize len;
405 jboolean isCopy=JNI_FALSE;
406
407 /*
408 * If the length of the byte array is 0 just return a null
409 */
410 len = (*env)->GetArrayLength(env, bytes);
411 if (len == 0) {
412 return NULL;
413 }
414
415 value = (*env)->GetByteArrayElements(env, bytes, &isCopy);
416 if (JNU_IsNull(env, value)) {
417 return NULL;
418 }
419
420 AWT_LOCK();
421
422 tp.encoding = XInternAtom(awt_display, "STRING", False);
423 tp.value = (unsigned char *)value;
424 tp.nitems = len;
425 tp.format = 8;
426
427 /*
428 * Convert the byte stream into a list of X11 strings
429 */
430 if (XTextPropertyToStringList(&tp, &strings, &nstrings) == 0 ||
431 nstrings == 0)
432 {
433 (*env)->ReleaseByteArrayElements(env, bytes, value, JNI_ABORT);
434 AWT_UNLOCK();
435 return NULL;
436 }
437
438 (*env)->ReleaseByteArrayElements(env, bytes, value, JNI_ABORT);
439
440 filenames = (*env)->NewObjectArray(env, nstrings, string, NULL);
441
442 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
443 (*env)->ExceptionDescribe(env);
444 (*env)->ExceptionClear(env);
445 goto wayout;
446 }
447
448 if (JNU_IsNull(env, filenames)) {
449 goto wayout;
450 }
451
452 /*
453 * Actuall conversion code per X11 String
454 */
455 for (i = 0; i < nstrings; i++) {
456 jstring string = (*env)->NewStringUTF(env,
457 (const char *)strings[i]);
458 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
459 (*env)->ExceptionDescribe(env);
460 (*env)->ExceptionClear(env);
461 goto wayout;
462 }
463
464 if (JNU_IsNull(env, string)) {
465 goto wayout;
466 }
467
468 (*env)->SetObjectArrayElement(env, filenames, i, string);
469
470 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
471 (*env)->ExceptionDescribe(env);
472 (*env)->ExceptionClear(env);
473 goto wayout;
474 }
475
476 (*env)->DeleteLocalRef(env, string);
477 }
478
479 ret = filenames;
480 wayout:
481 /*
482 * Clean up and return
483 */
484 XFreeStringList(strings);
485 AWT_UNLOCK();
486 return ret;
487}
488
489DECLARE_JAVA_CLASS(dataTransfererClazz, "sun/awt/datatransfer/DataTransferer")
490
491/**
492 * Returns a local reference to the singleton DataTransferer instance.
493 * The caller should delete the reference when done.
494 */
495static jobject
496get_data_transferer(JNIEnv* env) {
497 jobject transferer = NULL;
498
499 DECLARE_STATIC_OBJECT_JAVA_METHOD(getInstanceMethodID, dataTransfererClazz,
500 "getInstance",
501 "()Lsun/awt/datatransfer/DataTransferer;");
502
503 transferer = (*env)->CallStaticObjectMethod(env, clazz, getInstanceMethodID);
504
505 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
506 (*env)->ExceptionDescribe(env);
507 (*env)->ExceptionClear(env);
508 }
509
510 DASSERT(!JNU_IsNull(env, transferer));
511
512 return transferer;
513}
514
515static jobject
516call_convertData(JNIEnv* env, jobject source, jobject contents, jlong format,
517 jobject formatMap) {
518 jobject transferer = get_data_transferer(env);
519 jobject ret = NULL;
520 DECLARE_OBJECT_JAVA_METHOD(convertDataMethodID, dataTransfererClazz,
521 "convertData",
522 "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B");
523
524 ret = (*env)->CallObjectMethod(env, transferer, convertDataMethodID,
525 source, contents, format, formatMap,
526 awt_currentThreadIsPrivileged(env));
527
528 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
529 (*env)->ExceptionDescribe(env);
530 (*env)->ExceptionClear(env);
531 }
532
533 (*env)->DeleteLocalRef(env, transferer);
534
535 return ret;
536}
537
538static void
539process_convert_data_requests() {
540 JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_4);
541 jobject transferer = get_data_transferer(env);
542
543 DECLARE_VOID_JAVA_METHOD(processDataConversionRequestsMethodID,
544 dataTransfererClazz,
545 "processDataConversionRequests",
546 "()V");
547
548 (*env)->CallVoidMethod(env, transferer,
549 processDataConversionRequestsMethodID);
550
551 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
552 (*env)->ExceptionDescribe(env);
553 (*env)->ExceptionClear(env);
554 }
555
556 (*env)->DeleteLocalRef(env, transferer);
557}
558
559Boolean
560awt_convertData(Widget w, Atom * selection, Atom * target, Atom * type,
561 XtPointer * value, unsigned long *length, int32_t *format) {
562 JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
563 Display* dpy = XtDisplay(w);
564 awt_convertDataCallbackStruct* structPtr = NULL;
565
566 if (XFindContext(dpy, *selection, awt_convertDataContext,
567 (XPointer*)&structPtr) == XCNOMEM || structPtr == NULL) {
568 return False;
569 }
570
571 if ((*env)->PushLocalFrame(env, 2) < 0) {
572 (*env)->ExceptionDescribe(env);
573 (*env)->ExceptionClear(env);
574 return False;
575 }
576
577 if (*target == XA_TARGETS) {
578 jlongArray formats = structPtr->formats;
579 jsize count;
580 jlong* targets;
581 jboolean isCopy;
582
583#ifndef _LP64 /* Atom and jlong are different sizes in the 32-bit build */
584 Atom* aValue;
585 jlong* saveTargets;
586 jsize i;
587#endif
588
589 if (JNU_IsNull(env, formats)) {
590 (*env)->PopLocalFrame(env, NULL);
591 return False;
592 }
593
594 count = (*env)->GetArrayLength(env, formats);
595 if (count == 0) {
596 (*env)->PopLocalFrame(env, NULL);
597 return False;
598 }
599
600 targets = (*env)->GetLongArrayElements(env, formats, &isCopy);
601
602 *type = XA_ATOM;
603 *format = 32;
604
605#ifdef _LP64
606 *value = XtMalloc(count * sizeof(Atom));
607 memcpy((void *)*value, (void *)targets, count * sizeof(Atom));
608#else
609 *value = aValue = (Atom *)XtMalloc(count * sizeof(Atom));
610 saveTargets = targets;
611 for (i = 0; i < count; i++, aValue++, targets++) {
612 *aValue = (Atom)*targets;
613 }
614 targets = saveTargets;
615#endif
616 (*env)->ReleaseLongArrayElements(env, formats, targets, JNI_ABORT);
617
618 *length = count;
619
620 } else if (*target == XInternAtom(dpy, _XA_DELETE, False)) {
621
622 /*
623 * acknowledge the DELETE target here ... the "delete" semantic
624 * of move will take place after the drop is complete.
625 */
626
627 *type = XInternAtom(dpy, _XA_NULL, False);
628 *length = 0;
629 *value = (XtPointer)NULL;
630 /* Uninitialized format can cause crash in Xt conversion code. */
631 *format = 8;
632 } else if (*target == XInternAtom(dpy, _XA_HOSTNAME, False)) {
633 struct utsname name;
634 XTextProperty tp;
635
636 uname(&name);
637
638 if (!XStringListToTextProperty((char **)&name.nodename, 1, &tp)) {
639 (*env)->PopLocalFrame(env, NULL);
640 return False;
641 }
642
643 *value = (XtPointer)XtCalloc(tp.nitems, sizeof(char));
644
645 memcpy((void *)*value, (const void *)tp.value, tp.nitems);
646
647 XFree((void *)tp.value);
648
649 *type = tp.encoding;
650 *length = tp.nitems + 1;
651 *format = tp.format;
652 } else if (*target == XInternAtom(dpy, _XA_FILENAME, False) ||
653 *target == XInternAtom(dpy, _DT_FILENAME, False)) {
654
655 /*
656 * Convert the internal representation to an File Name.
657 * The data returned from dataConvert is a an array of
658 * null separated bytes. Each series of bytes is a string
659 * that is then converted to an XString which are then put
660 * into an XStringList and put into an XTextProperty for
661 * usage in other programs.
662 *
663 * It would be desireable to have dataConvert to this conversion
664 * but it isn't possible to return a byte array that represents
665 * the XTextProperty.
666 */
667 jbyteArray data;
668
669 /*
670 * Fix for 4513976.
671 * Type None should be used instead of XT_CONVERT_FAIL
672 * to report conversion failure.
673 */
674 /* assume forthcoming error */
675 *type = None;
676 *value = (XtPointer)NULL;
677 *length = 0;
678 *format = 8;
679
680 data = call_convertData(env, structPtr->source, structPtr->transferable,
681 (jlong)*target, structPtr->formatMap);
682
683 /* error test */
684 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
685 (*env)->ExceptionDescribe(env);
686 (*env)->ExceptionClear(env);
687 (*env)->PopLocalFrame(env, NULL);
688 return False;
689 }
690 if (JNU_IsNull(env, data)) {
691 (*env)->PopLocalFrame(env, NULL);
692 return False;
693 }
694
695 if (convertFileType(data, type, value, length, format) == False) {
696 (*env)->PopLocalFrame(env, NULL);
697 return False;
698 }
699 } else {
700 jbyteArray bytes = NULL;
701 jbyte* copy = NULL;
702
703 /*
704 * Fix for 4513976.
705 * Type None should be used instead of XT_CONVERT_FAIL
706 * to report conversion failure.
707 */
708 *type = None; /* assume forthcoming error */
709 *value = (XtPointer)NULL;
710 *length = 0;
711 *format = 8;
712
713 bytes = call_convertData(env, structPtr->source, structPtr->transferable,
714 (jlong)*target, structPtr->formatMap);
715
716 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
717 (*env)->ExceptionDescribe(env);
718 (*env)->ExceptionClear(env);
719 (*env)->PopLocalFrame(env, NULL);
720 return False;
721 }
722
723 if (bytes == NULL) {
724 (*env)->PopLocalFrame(env, NULL);
725 return False;
726 } else {
727 jsize len = (*env)->GetArrayLength(env, bytes);
728
729 if (len == 0) {
730 *type = *target;
731 *format = 8;
732 (*env)->PopLocalFrame(env, NULL);
733 return True;
734 }
735
736 copy = (jbyte*)XtCalloc(1, len * sizeof(jbyte));
737 if (copy == (jbyte*)NULL) {
738 JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
739 (*env)->PopLocalFrame(env, NULL);
740 return False;
741 }
742
743 (*env)->GetByteArrayRegion(env, (jbyteArray)bytes, 0, len, copy);
744
745 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
746 (*env)->ExceptionDescribe(env);
747 (*env)->ExceptionClear(env);
748 XtFree((void *)copy);
749 (*env)->PopLocalFrame(env, NULL);
750 return False;
751 }
752
753 *value = (XtPointer)copy;
754 *type = *target;
755 *length = len;
756 *format = 8;
757 }
758 }
759
760 (*env)->PopLocalFrame(env, NULL);
761 return True;
762}
763
764
765jlongArray
766getSelectionTargetsHelper(JNIEnv* env, XtPointer value, unsigned long length)
767{
768 Atom* targets = (Atom*)value;
769 jlongArray targetArray = NULL;
770 jlong* checkedTargets = NULL;
771 size_t count = 0, i = 0, j = 0;
772
773 /* Get rid of zero atoms if there are any. */
774 for (; i < length; i++) {
775 if (targets[i] != 0) {
776 count++;
777 }
778 }
779 checkedTargets = calloc(count, sizeof(jlong));
780 if (checkedTargets == NULL) {
781 JNU_ThrowOutOfMemoryError(env, "");
782 (*env)->ExceptionDescribe(env);
783 (*env)->ExceptionClear(env);
784 } else {
785 for (i = 0; i < length; i++) {
786 if (targets[i] != 0) {
787 checkedTargets[j++] = targets[i];
788 }
789 }
790
791 DASSERT(j == count);
792
793 if ((*env)->EnsureLocalCapacity(env, 1) >= 0) {
794
795 targetArray = (*env)->NewLongArray(env, count);
796
797 if (!JNU_IsNull(env, targetArray)) {
798 (*env)->SetLongArrayRegion(env, targetArray, 0, count,
799 checkedTargets);
800
801 if ((*env)->ExceptionCheck(env)) {
802 (*env)->ExceptionDescribe(env);
803 (*env)->ExceptionClear(env);
804
805 (*env)->DeleteLocalRef(env, targetArray);
806 targetArray = NULL;
807 }
808 }
809 }
810 free(checkedTargets);
811 }
812
813 return targetArray;
814}
815
816static void
817get_selection_targets_callback(Widget w, XtPointer client_data, Atom* selection,
818 Atom* type, XtPointer value,
819 unsigned long* length, int32_t* format) {
820 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
821 jobject* pReturnArray = (jobject*)client_data;
822 SelectionStatus status = SelectionFailure;
823
824 /*
825 * It is highly unlikely that TARGETS will ever be passed even though that
826 * was what was requested. However, XA_ATOM ("ATOM") is likely.
827 * Actually they are the same so treat them as such. See XToolKit
828 * Intrinsic Manual on XtSelectionCallbackProc for more details on type.
829 */
830 if (*type == XA_TARGETS || *type == XA_ATOM) {
831 jlongArray targetArray = getSelectionTargetsHelper(env, value, *length);
832 if (!JNU_IsNull(env, targetArray)) {
833 *pReturnArray = (*env)->NewGlobalRef(env, targetArray);
834 status = SelectionSuccess;
835 (*env)->DeleteLocalRef(env, targetArray);
836 }
837 } else if (*type == XT_CONVERT_FAIL) {
838 status = SelectionOwnerTimedOut;
839 } else {
840 /*
841 * A part of the fix for 4259272.
842 * Actually Xt Intrinsics says about XtSelectionCallback that
843 * "if there is no owner for the specified selection, or that owner
844 * cannot convert the selected data to the requested type, then this
845 * callback is called with value NULL and length zero".
846 * But we report success if type is not TARGETS, XA_ATOM or XT_CONVERT_FAIL,
847 * and we should not change this behaviour. We just return zero-length
848 * array instead of null, because null denotes that we could not get
849 * selection targets at the time of tracking changes of available on
850 * the selection data flavors.
851 */
852 jlongArray targetArray = (*env)->NewLongArray(env, 0);
853 *pReturnArray = (*env)->NewGlobalRef(env, targetArray);
854 /*
855 * Fix for 4655996.
856 * Report success if there is no owner for this selection or the owner
857 * fails to provide target types.
858 */
859 status = SelectionSuccess;
860 (*env)->DeleteLocalRef(env, targetArray);
861 }
862
863 if (value != NULL) {
864 XtFree(value);
865 value = NULL;
866 }
867
868 set_selection_status(status);
869}
870
871static void
872get_selection_data_callback(Widget w, XtPointer client_data, Atom * selection,
873 Atom * type, XtPointer value, unsigned long *length,
874 int32_t *format) {
875 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
876 jobject* pData = (jobject*)client_data;
877 SelectionStatus status = SelectionFailure;
878
879 if (*type == XT_CONVERT_FAIL) {
880 status = SelectionOwnerTimedOut;
881 } else if (*type != None) {
882 if ((*env)->EnsureLocalCapacity(env, 1) >= 0) {
883 jsize size = (*length <= INT_MAX) ? *length : INT_MAX;
884 jbyteArray array = (*env)->NewByteArray(env, size);
885
886 if (!JNU_IsNull(env, array)) {
887 (*env)->SetByteArrayRegion(env, array, 0, size, (jbyte*)value);
888 if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
889 (*env)->ExceptionDescribe(env);
890 (*env)->ExceptionClear(env);
891 } else {
892 *pData = (*env)->NewGlobalRef(env, array);
893 status = SelectionSuccess;
894 }
895
896 (*env)->DeleteLocalRef(env, array);
897 }
898 }
899 }
900
901 if (value != NULL) {
902 XtFree(value);
903 value = NULL;
904 }
905
906 set_selection_status(status);
907}
908
909static int32_t
910wait_for_selection_event(void *data) {
911 process_convert_data_requests();
912 return get_selection_status() != SelectionPending;
913}
914
915jlongArray
916get_selection_targets(JNIEnv *env, Atom selection, Time time_stamp) {
917 jlongArray ret = NULL;
918 jlongArray targets = NULL;
919 SelectionStatus status = SelectionPending;
920
921 AWT_LOCK();
922
923 XtAppSetSelectionTimeout(awt_appContext,
924 JNU_CallStaticMethodByName(env, NULL, "sun/awt/UNIXToolkit",
925 "getDatatransferTimeout", "()I").i);
926
927 set_selection_status(SelectionPending);
928 XtGetSelectionValue(awt_root_shell, selection, XA_TARGETS,
929 get_selection_targets_callback, (XtPointer)&targets,
930 time_stamp);
931
932 awt_MToolkit_modalWait(wait_for_selection_event, NULL);
933 status = get_selection_status();
934
935 AWT_FLUSH_UNLOCK();
936
937 if (!JNU_IsNull(env, targets)) {
938 ret = (*env)->NewLocalRef(env, targets);
939 (*env)->DeleteGlobalRef(env, targets);
940 }
941
942 switch (status) {
943 case SelectionSuccess:
944 break;
945 case SelectionFailure:
946 JNU_ThrowByName(env, "java/lang/IllegalStateException",
947 "Failed to get selection targets");
948 break;
949 case SelectionOwnerTimedOut:
950 // return an empty array of targets if the selection owner timed out
951 ret = (*env)->NewLongArray(env, 0);
952 break;
953 default:
954 JNU_ThrowByName(env, "java/lang/IllegalStateException",
955 "Unexpected selection status");
956 break;
957 }
958
959 return ret;
960}
961
962jbyteArray
963get_selection_data(JNIEnv *env, Atom selection, Atom target, Time time_stamp) {
964 jbyteArray ret = NULL;
965 jbyteArray data = NULL;
966 SelectionStatus status = SelectionPending;
967
968 AWT_LOCK();
969
970 XtAppSetSelectionTimeout(awt_appContext,
971 JNU_CallStaticMethodByName(env, NULL, "sun/awt/UNIXToolkit",
972 "getDatatransferTimeout", "()I").i);
973
974 set_selection_status(SelectionPending);
975 XtGetSelectionValue(awt_root_shell, selection, target,
976 get_selection_data_callback,
977 (XtPointer)&data, time_stamp);
978
979 awt_MToolkit_modalWait(wait_for_selection_event, NULL);
980 status = get_selection_status();
981
982 AWT_FLUSH_UNLOCK();
983
984 if (!JNU_IsNull(env, data)) {
985 ret = (*env)->NewLocalRef(env, data);
986 (*env)->DeleteGlobalRef(env, data);
987 }
988
989 switch (status) {
990 case SelectionSuccess:
991 break;
992 case SelectionFailure:
993 JNU_ThrowIOException(env, "Failed to get selection data");
994 break;
995 case SelectionOwnerTimedOut:
996 JNU_ThrowIOException(env, "Selection owner timed out");
997 break;
998 default:
999 JNU_ThrowIOException(env, "Unexpected selection status");
1000 break;
1001 }
1002
1003 return ret;
1004}
1005
1006void
1007awt_cleanupConvertDataContext(JNIEnv *env, Atom selectionAtom) {
1008 awt_convertDataCallbackStruct* structPtr = NULL;
1009
1010 if (XFindContext(awt_display, selectionAtom, awt_convertDataContext,
1011 (XPointer*)&structPtr) == 0 && structPtr != NULL) {
1012
1013 (*env)->DeleteGlobalRef(env, structPtr->source);
1014 (*env)->DeleteGlobalRef(env, structPtr->transferable);
1015 (*env)->DeleteGlobalRef(env, structPtr->formatMap);
1016 (*env)->DeleteGlobalRef(env, structPtr->formats);
1017 free(structPtr);
1018 }
1019 /*
1020 * Xlib Programming Manual says that it is better to erase
1021 * the current entry with XDeleteContext() before XSaveContext().
1022 */
1023 XDeleteContext(awt_display, selectionAtom, awt_convertDataContext);
1024 if (XSaveContext(awt_display, selectionAtom, awt_convertDataContext,
1025 (XPointer)NULL) == XCNOMEM) {
1026 JNU_ThrowInternalError(env, "XError");
1027 (*env)->ExceptionDescribe(env);
1028 (*env)->ExceptionClear(env);
1029 }
1030}
1031
1032static Bool exitSecondaryLoop = True;
1033
1034/*
1035 * This predicate procedure allows the Toolkit thread to process specific events
1036 * while it is blocked waiting for the event dispatch thread to process
1037 * a SunDropTargetEvent. We need this to prevent deadlock when the client code
1038 * processing SunDropTargetEvent sets or gets the contents of the system
1039 * clipboard/selection. In this case the event dispatch thread waits for the
1040 * Toolkit thread to process PropertyNotify or SelectionNotify events.
1041 */
1042static Bool
1043secondary_loop_event(Display* dpy, XEvent* event, char* arg) {
1044 return (event->type == SelectionNotify ||
1045 event->type == SelectionClear ||
1046 event->type == PropertyNotify) ? True : False;
1047}
1048
1049
1050JNIEXPORT void JNICALL
1051Java_sun_awt_motif_MToolkitThreadBlockedHandler_enter(JNIEnv *env, jobject this) {
1052 DASSERT(exitSecondaryLoop && awt_currentThreadIsPrivileged(env));
1053 exitSecondaryLoop = False;
1054 while (!exitSecondaryLoop) {
1055 XEvent event;
1056 while (XCheckIfEvent(awt_display, &event, secondary_loop_event, NULL)) {
1057 XtDispatchEvent(&event);
1058 }
1059 AWT_WAIT(AWT_DND_POLL_INTERVAL);
1060 }
1061}
1062
1063JNIEXPORT void JNICALL
1064Java_sun_awt_motif_MToolkitThreadBlockedHandler_exit(JNIEnv *env, jobject this) {
1065 DASSERT(!exitSecondaryLoop && !awt_currentThreadIsPrivileged(env));
1066 exitSecondaryLoop = True;
1067 AWT_NOTIFY_ALL();
1068}