| /* |
| * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| #ifdef HEADLESS |
| #error This file should not be included in headless library |
| #endif |
| |
| #include "awt_p.h" |
| #include <X11/Intrinsic.h> |
| #include <X11/Xutil.h> |
| |
| #include <sys/utsname.h> |
| |
| #include <jni.h> |
| #include <jni_util.h> |
| |
| #include "sun_awt_datatransfer_DataTransferer.h" |
| #include "sun_awt_motif_MDataTransferer.h" |
| |
| #include "awt_XmDnD.h" |
| #include "awt_DataTransferer.h" |
| |
| static jclass string; |
| |
| XContext awt_convertDataContext = 0; |
| |
| Atom XA_TARGETS; |
| |
| extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs; |
| |
| typedef enum { |
| SelectionPending, |
| SelectionSuccess, |
| SelectionFailure, |
| SelectionOwnerTimedOut |
| } SelectionStatus; |
| |
| /* Should only be accessed by the current owner of AWT_LOCK. */ |
| static SelectionStatus globalSelectionStatus = SelectionPending; |
| |
| static SelectionStatus get_selection_status() { |
| return globalSelectionStatus; |
| } |
| |
| static void set_selection_status(SelectionStatus status) { |
| globalSelectionStatus = status; |
| } |
| |
| static void |
| selection_request_filter(Widget widget, XtPointer closure, XEvent *event, |
| Boolean *cont) { |
| if (event->type == SelectionRequest) { |
| Window awt_root_window = XtWindow(awt_root_shell); |
| Atom selection = event->xselectionrequest.selection; |
| Window owner = XGetSelectionOwner(event->xany.display, selection); |
| |
| if (owner != awt_root_window) { |
| XSelectionEvent notify; |
| |
| notify.type = SelectionNotify; |
| notify.display = event->xselectionrequest.display; |
| notify.requestor = event->xselectionrequest.requestor; |
| notify.selection = event->xselectionrequest.selection; |
| notify.time = event->xselectionrequest.time; |
| notify.target = event->xselectionrequest.target; |
| notify.property = None; |
| |
| XSendEvent(notify.display, notify.requestor, False, |
| (unsigned long)0, (XEvent*)¬ify); |
| *cont = False; |
| } |
| } |
| } |
| |
| /** |
| * global function to initialize this client as a Dynamic-only app. |
| * |
| * gets called once during toolkit initialization. |
| */ |
| |
| void awt_initialize_DataTransferer() { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jclass stringClassLocal = NULL; |
| |
| DASSERT(string == NULL); |
| |
| stringClassLocal = (*env)->FindClass(env, "java/lang/String"); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| DASSERT(False); |
| } |
| |
| if (JNU_IsNull(env, stringClassLocal)) return; |
| |
| string = (*env)->NewGlobalRef(env, stringClassLocal); /* never freed! */ |
| (*env)->DeleteLocalRef(env, stringClassLocal); |
| |
| if (JNU_IsNull(env, string)) { |
| JNU_ThrowOutOfMemoryError(env, ""); |
| return; |
| } |
| |
| DASSERT(awt_convertDataContext == 0); |
| awt_convertDataContext = XUniqueContext(); |
| DASSERT(awt_convertDataContext != 0); |
| |
| /* |
| * Fixes for 4513976 and 4818143. |
| */ |
| XtAppSetSelectionTimeout(awt_appContext, |
| JNU_CallStaticMethodByName(env, NULL, "sun/awt/UNIXToolkit", |
| "getDatatransferTimeout", "()I").i); |
| |
| /* |
| * Xt selection machinery doesn't respond to SelectionRequests if the |
| * event arrives on a widget that is not the current selection owner. |
| * This can happen if XtDisownSelection was called when SelectionRequest was |
| * already on the native queue. |
| * If the requestor is another JVM, it hangs for the selection timeout |
| * as SelectionNotify is never sent. |
| * We install an event handler that filters out SelectionRequests if the |
| * awt_root_shell is not the current selection owner. |
| */ |
| XtAddEventHandler(awt_root_shell, (EventMask)0, True, |
| selection_request_filter, NULL); |
| |
| XA_TARGETS = XInternAtom(awt_display, "TARGETS", False); |
| } |
| |
| /* |
| * Single routine to convert to target FILE_NAME or _DT_FILENAME |
| */ |
| Boolean |
| convertFileType(jbyteArray data, Atom * type, XtPointer * value, |
| unsigned long *length, int32_t *format) |
| { |
| /* |
| * Convert the internal representation to an File Name. |
| * The data passed is an array of |
| * null separated bytes. Each series of bytes is a string |
| * that is then converted to an XString which are then put |
| * into an XStringList and put into an XTextProperty for |
| * usage in other programs. |
| * |
| * It would be desireable to have dataConvert to this conversion |
| * but it isn't possible to return a byte array that represents |
| * the XTextProperty. |
| */ |
| |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jboolean isCopy=JNI_FALSE; |
| XTextProperty tp; |
| jsize len; |
| jsize strings = 0; |
| jsize i; |
| char** stringList; |
| Status s; |
| jbyte* bytes; |
| char* start; |
| size_t slen; |
| char* utf; |
| |
| if ((*env)->PushLocalFrame(env, 16) < 0) { |
| return False; |
| } |
| |
| /* convert the data to an Array of Byte Elements */ |
| bytes = (*env)->GetByteArrayElements(env, data, &isCopy); |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| if (JNU_IsNull(env, bytes)) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| /* Get the length of the area */ |
| len = (*env)->GetArrayLength(env, data); |
| if (len == 0) { |
| (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| /* |
| * determine the number of lists. The byteArray is null separated list of |
| * strings. |
| */ |
| for (i = 0; i < len; i++) { |
| if (bytes[i] == '\0') { |
| strings++; |
| } |
| } |
| |
| /* Allocate an X11 string list */ |
| stringList = (char **)XtCalloc(strings, sizeof(char *)); |
| if (stringList == (char**)NULL) { |
| (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| for (i = 0; i < strings; i++) { |
| if (i == 0) { |
| start = (char*)bytes; |
| } else { |
| start = (char*)&bytes[slen]; |
| } |
| |
| /* |
| * if start is a NULL we're at the end of the list |
| * We'll just a have null entry on the end of the list |
| */ |
| if (start[0] == '\0') { |
| stringList[i] = NULL; |
| continue; |
| } |
| slen = strlen(start) + 1; |
| |
| stringList[i] = (char*)XtCalloc(slen, sizeof(char)); |
| |
| if (stringList[i] == (char *)NULL) { |
| jsize j; |
| |
| (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); |
| |
| for (j = 0; j < i; j++) { |
| XtFree((void *)stringList[j]); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| |
| return False; |
| } |
| |
| memcpy((void *)stringList[i], (const void*)start, slen); |
| } |
| |
| (*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT); |
| s = XStringListToTextProperty(stringList, strings, &tp); |
| |
| /* free the strings that were created */ |
| for (i = 0; i < strings; i++) { |
| if (stringList[i] != NULL) { |
| XtFree((void*)stringList[i]); |
| } |
| } |
| |
| XtFree((void*)stringList); |
| |
| if (s == 0) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| *value = (XtPointer)XtCalloc(tp.nitems, sizeof(char)); |
| |
| if (*value == (XtPointer)NULL) { |
| XFree((void*)tp.value); |
| |
| (*env)->PopLocalFrame(env, NULL); |
| |
| return False; |
| } |
| |
| memcpy((void *)(*value), (const void *)tp.value, tp.nitems); |
| |
| XFree((void*)tp.value); |
| |
| *length = tp.nitems; |
| *type = tp.encoding; |
| *format = tp.format; |
| (*env)->PopLocalFrame(env, NULL); |
| return True; |
| } |
| |
| /* |
| * Class: sun_awt_motif_MDataTransferer |
| * Method: getAtomForTarget |
| * Signature: (Ljava/lang/String;)J |
| */ |
| |
| JNIEXPORT jlong JNICALL |
| Java_sun_awt_motif_MDataTransferer_getAtomForTarget(JNIEnv *env, |
| jclass cls, |
| jstring targetString) |
| { |
| Atom target; |
| char *target_str; |
| |
| if (JNU_IsNull(env, targetString)) { |
| JNU_ThrowNullPointerException(env, "NullPointerException"); |
| return -1; |
| } |
| target_str = (char *) JNU_GetStringPlatformChars(env, targetString, NULL); |
| |
| AWT_LOCK(); |
| |
| target = XInternAtom(awt_display, target_str, False); |
| |
| AWT_UNLOCK(); |
| |
| JNU_ReleaseStringPlatformChars(env, targetString, |
| (const char *) target_str); |
| return target; |
| } |
| |
| /* |
| * Class: sun_awt_motif_MDataTransferer |
| * Method: getTargetNameForAtom |
| * Signature: (J)Ljava/lang/String; |
| */ |
| |
| JNIEXPORT jstring JNICALL |
| Java_sun_awt_motif_MDataTransferer_getTargetNameForAtom(JNIEnv *env, |
| jclass cls, |
| jlong atom) |
| { |
| jstring targetString; |
| char *name; |
| |
| AWT_LOCK(); |
| |
| name = XGetAtomName(awt_display, (Atom) atom); |
| |
| if (name == NULL) { |
| JNU_ThrowNullPointerException(env, "Failed to retrieve atom name."); |
| AWT_UNLOCK(); |
| return NULL; |
| } |
| |
| targetString = (*env)->NewStringUTF(env, (const char *)name); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| XFree (name); |
| AWT_UNLOCK(); |
| return NULL; |
| } |
| |
| if (JNU_IsNull(env, targetString)) { |
| JNU_ThrowNullPointerException(env, "Failed to create a string."); |
| XFree (name); |
| AWT_UNLOCK(); |
| return NULL; |
| } |
| |
| XFree (name); |
| |
| AWT_UNLOCK(); |
| return targetString; |
| } |
| |
| /* |
| * Class: sun_awt_datatransfer_DataTransferer |
| * Method: dragQueryFile |
| * Signature: ([B)[Ljava/lang/String; |
| * |
| * This method converts a byte array that came from File most likely from a |
| * drag operation into a String array. |
| */ |
| |
| JNIEXPORT jobjectArray JNICALL |
| Java_sun_awt_motif_MDataTransferer_dragQueryFile |
| (JNIEnv *env, jobject this, jbyteArray bytes) |
| { |
| XTextProperty tp; |
| jbyte *value; |
| |
| char** strings = (char **)NULL; |
| int32_t nstrings = 0; |
| jobject filenames; |
| jobject ret = NULL; |
| int32_t i; |
| jsize len; |
| jboolean isCopy=JNI_FALSE; |
| |
| /* |
| * If the length of the byte array is 0 just return a null |
| */ |
| len = (*env)->GetArrayLength(env, bytes); |
| if (len == 0) { |
| return NULL; |
| } |
| |
| value = (*env)->GetByteArrayElements(env, bytes, &isCopy); |
| if (JNU_IsNull(env, value)) { |
| return NULL; |
| } |
| |
| AWT_LOCK(); |
| |
| tp.encoding = XInternAtom(awt_display, "STRING", False); |
| tp.value = (unsigned char *)value; |
| tp.nitems = len; |
| tp.format = 8; |
| |
| /* |
| * Convert the byte stream into a list of X11 strings |
| */ |
| if (XTextPropertyToStringList(&tp, &strings, &nstrings) == 0 || |
| nstrings == 0) |
| { |
| (*env)->ReleaseByteArrayElements(env, bytes, value, JNI_ABORT); |
| AWT_UNLOCK(); |
| return NULL; |
| } |
| |
| (*env)->ReleaseByteArrayElements(env, bytes, value, JNI_ABORT); |
| |
| filenames = (*env)->NewObjectArray(env, nstrings, string, NULL); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| goto wayout; |
| } |
| |
| if (JNU_IsNull(env, filenames)) { |
| goto wayout; |
| } |
| |
| /* |
| * Actuall conversion code per X11 String |
| */ |
| for (i = 0; i < nstrings; i++) { |
| jstring string = (*env)->NewStringUTF(env, |
| (const char *)strings[i]); |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| goto wayout; |
| } |
| |
| if (JNU_IsNull(env, string)) { |
| goto wayout; |
| } |
| |
| (*env)->SetObjectArrayElement(env, filenames, i, string); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| goto wayout; |
| } |
| |
| (*env)->DeleteLocalRef(env, string); |
| } |
| |
| ret = filenames; |
| wayout: |
| /* |
| * Clean up and return |
| */ |
| XFreeStringList(strings); |
| AWT_UNLOCK(); |
| return ret; |
| } |
| |
| DECLARE_JAVA_CLASS(dataTransfererClazz, "sun/awt/datatransfer/DataTransferer") |
| |
| /** |
| * Returns a local reference to the singleton DataTransferer instance. |
| * The caller should delete the reference when done. |
| */ |
| static jobject |
| get_data_transferer(JNIEnv* env) { |
| jobject transferer = NULL; |
| |
| DECLARE_STATIC_OBJECT_JAVA_METHOD(getInstanceMethodID, dataTransfererClazz, |
| "getInstance", |
| "()Lsun/awt/datatransfer/DataTransferer;"); |
| |
| transferer = (*env)->CallStaticObjectMethod(env, clazz, getInstanceMethodID); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| DASSERT(!JNU_IsNull(env, transferer)); |
| |
| return transferer; |
| } |
| |
| static jobject |
| call_convertData(JNIEnv* env, jobject source, jobject contents, jlong format, |
| jobject formatMap) { |
| jobject transferer = get_data_transferer(env); |
| jobject ret = NULL; |
| DECLARE_OBJECT_JAVA_METHOD(convertDataMethodID, dataTransfererClazz, |
| "convertData", |
| "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B"); |
| |
| ret = (*env)->CallObjectMethod(env, transferer, convertDataMethodID, |
| source, contents, format, formatMap, |
| awt_currentThreadIsPrivileged(env)); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->DeleteLocalRef(env, transferer); |
| |
| return ret; |
| } |
| |
| static void |
| process_convert_data_requests() { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_4); |
| jobject transferer = get_data_transferer(env); |
| |
| DECLARE_VOID_JAVA_METHOD(processDataConversionRequestsMethodID, |
| dataTransfererClazz, |
| "processDataConversionRequests", |
| "()V"); |
| |
| (*env)->CallVoidMethod(env, transferer, |
| processDataConversionRequestsMethodID); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->DeleteLocalRef(env, transferer); |
| } |
| |
| Boolean |
| awt_convertData(Widget w, Atom * selection, Atom * target, Atom * type, |
| XtPointer * value, unsigned long *length, int32_t *format) { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| Display* dpy = XtDisplay(w); |
| awt_convertDataCallbackStruct* structPtr = NULL; |
| |
| if (XFindContext(dpy, *selection, awt_convertDataContext, |
| (XPointer*)&structPtr) == XCNOMEM || structPtr == NULL) { |
| return False; |
| } |
| |
| if ((*env)->PushLocalFrame(env, 2) < 0) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| return False; |
| } |
| |
| if (*target == XA_TARGETS) { |
| jlongArray formats = structPtr->formats; |
| jsize count; |
| jlong* targets; |
| jboolean isCopy; |
| |
| #ifndef _LP64 /* Atom and jlong are different sizes in the 32-bit build */ |
| Atom* aValue; |
| jlong* saveTargets; |
| jsize i; |
| #endif |
| |
| if (JNU_IsNull(env, formats)) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| count = (*env)->GetArrayLength(env, formats); |
| if (count == 0) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| targets = (*env)->GetLongArrayElements(env, formats, &isCopy); |
| |
| *type = XA_ATOM; |
| *format = 32; |
| |
| #ifdef _LP64 |
| *value = XtMalloc(count * sizeof(Atom)); |
| memcpy((void *)*value, (void *)targets, count * sizeof(Atom)); |
| #else |
| *value = aValue = (Atom *)XtMalloc(count * sizeof(Atom)); |
| saveTargets = targets; |
| for (i = 0; i < count; i++, aValue++, targets++) { |
| *aValue = (Atom)*targets; |
| } |
| targets = saveTargets; |
| #endif |
| (*env)->ReleaseLongArrayElements(env, formats, targets, JNI_ABORT); |
| |
| *length = count; |
| |
| } else if (*target == XInternAtom(dpy, _XA_DELETE, False)) { |
| |
| /* |
| * acknowledge the DELETE target here ... the "delete" semantic |
| * of move will take place after the drop is complete. |
| */ |
| |
| *type = XInternAtom(dpy, _XA_NULL, False); |
| *length = 0; |
| *value = (XtPointer)NULL; |
| /* Uninitialized format can cause crash in Xt conversion code. */ |
| *format = 8; |
| } else if (*target == XInternAtom(dpy, _XA_HOSTNAME, False)) { |
| struct utsname name; |
| XTextProperty tp; |
| |
| uname(&name); |
| |
| if (!XStringListToTextProperty((char **)&name.nodename, 1, &tp)) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| *value = (XtPointer)XtCalloc(tp.nitems, sizeof(char)); |
| |
| memcpy((void *)*value, (const void *)tp.value, tp.nitems); |
| |
| XFree((void *)tp.value); |
| |
| *type = tp.encoding; |
| *length = tp.nitems + 1; |
| *format = tp.format; |
| } else if (*target == XInternAtom(dpy, _XA_FILENAME, False) || |
| *target == XInternAtom(dpy, _DT_FILENAME, False)) { |
| |
| /* |
| * Convert the internal representation to an File Name. |
| * The data returned from dataConvert is a an array of |
| * null separated bytes. Each series of bytes is a string |
| * that is then converted to an XString which are then put |
| * into an XStringList and put into an XTextProperty for |
| * usage in other programs. |
| * |
| * It would be desireable to have dataConvert to this conversion |
| * but it isn't possible to return a byte array that represents |
| * the XTextProperty. |
| */ |
| jbyteArray data; |
| |
| /* |
| * Fix for 4513976. |
| * Type None should be used instead of XT_CONVERT_FAIL |
| * to report conversion failure. |
| */ |
| /* assume forthcoming error */ |
| *type = None; |
| *value = (XtPointer)NULL; |
| *length = 0; |
| *format = 8; |
| |
| data = call_convertData(env, structPtr->source, structPtr->transferable, |
| (jlong)*target, structPtr->formatMap); |
| |
| /* error test */ |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| if (JNU_IsNull(env, data)) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| if (convertFileType(data, type, value, length, format) == False) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| } else { |
| jbyteArray bytes = NULL; |
| jbyte* copy = NULL; |
| |
| /* |
| * Fix for 4513976. |
| * Type None should be used instead of XT_CONVERT_FAIL |
| * to report conversion failure. |
| */ |
| *type = None; /* assume forthcoming error */ |
| *value = (XtPointer)NULL; |
| *length = 0; |
| *format = 8; |
| |
| bytes = call_convertData(env, structPtr->source, structPtr->transferable, |
| (jlong)*target, structPtr->formatMap); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| if (bytes == NULL) { |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } else { |
| jsize len = (*env)->GetArrayLength(env, bytes); |
| |
| if (len == 0) { |
| *type = *target; |
| *format = 8; |
| (*env)->PopLocalFrame(env, NULL); |
| return True; |
| } |
| |
| copy = (jbyte*)XtCalloc(1, len * sizeof(jbyte)); |
| if (copy == (jbyte*)NULL) { |
| JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| (*env)->GetByteArrayRegion(env, (jbyteArray)bytes, 0, len, copy); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| XtFree((void *)copy); |
| (*env)->PopLocalFrame(env, NULL); |
| return False; |
| } |
| |
| *value = (XtPointer)copy; |
| *type = *target; |
| *length = len; |
| *format = 8; |
| } |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| return True; |
| } |
| |
| |
| jlongArray |
| getSelectionTargetsHelper(JNIEnv* env, XtPointer value, unsigned long length) |
| { |
| Atom* targets = (Atom*)value; |
| jlongArray targetArray = NULL; |
| jlong* checkedTargets = NULL; |
| size_t count = 0, i = 0, j = 0; |
| |
| /* Get rid of zero atoms if there are any. */ |
| for (; i < length; i++) { |
| if (targets[i] != 0) { |
| count++; |
| } |
| } |
| checkedTargets = calloc(count, sizeof(jlong)); |
| if (checkedTargets == NULL) { |
| JNU_ThrowOutOfMemoryError(env, ""); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } else { |
| for (i = 0; i < length; i++) { |
| if (targets[i] != 0) { |
| checkedTargets[j++] = targets[i]; |
| } |
| } |
| |
| DASSERT(j == count); |
| |
| if ((*env)->EnsureLocalCapacity(env, 1) >= 0) { |
| |
| targetArray = (*env)->NewLongArray(env, count); |
| |
| if (!JNU_IsNull(env, targetArray)) { |
| (*env)->SetLongArrayRegion(env, targetArray, 0, count, |
| checkedTargets); |
| |
| if ((*env)->ExceptionCheck(env)) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| |
| (*env)->DeleteLocalRef(env, targetArray); |
| targetArray = NULL; |
| } |
| } |
| } |
| free(checkedTargets); |
| } |
| |
| return targetArray; |
| } |
| |
| static void |
| get_selection_targets_callback(Widget w, XtPointer client_data, Atom* selection, |
| Atom* type, XtPointer value, |
| unsigned long* length, int32_t* format) { |
| JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject* pReturnArray = (jobject*)client_data; |
| SelectionStatus status = SelectionFailure; |
| |
| /* |
| * It is highly unlikely that TARGETS will ever be passed even though that |
| * was what was requested. However, XA_ATOM ("ATOM") is likely. |
| * Actually they are the same so treat them as such. See XToolKit |
| * Intrinsic Manual on XtSelectionCallbackProc for more details on type. |
| */ |
| if (*type == XA_TARGETS || *type == XA_ATOM) { |
| jlongArray targetArray = getSelectionTargetsHelper(env, value, *length); |
| if (!JNU_IsNull(env, targetArray)) { |
| *pReturnArray = (*env)->NewGlobalRef(env, targetArray); |
| status = SelectionSuccess; |
| (*env)->DeleteLocalRef(env, targetArray); |
| } |
| } else if (*type == XT_CONVERT_FAIL) { |
| status = SelectionOwnerTimedOut; |
| } else { |
| /* |
| * A part of the fix for 4259272. |
| * Actually Xt Intrinsics says about XtSelectionCallback that |
| * "if there is no owner for the specified selection, or that owner |
| * cannot convert the selected data to the requested type, then this |
| * callback is called with value NULL and length zero". |
| * But we report success if type is not TARGETS, XA_ATOM or XT_CONVERT_FAIL, |
| * and we should not change this behaviour. We just return zero-length |
| * array instead of null, because null denotes that we could not get |
| * selection targets at the time of tracking changes of available on |
| * the selection data flavors. |
| */ |
| jlongArray targetArray = (*env)->NewLongArray(env, 0); |
| *pReturnArray = (*env)->NewGlobalRef(env, targetArray); |
| /* |
| * Fix for 4655996. |
| * Report success if there is no owner for this selection or the owner |
| * fails to provide target types. |
| */ |
| status = SelectionSuccess; |
| (*env)->DeleteLocalRef(env, targetArray); |
| } |
| |
| if (value != NULL) { |
| XtFree(value); |
| value = NULL; |
| } |
| |
| set_selection_status(status); |
| } |
| |
| static void |
| get_selection_data_callback(Widget w, XtPointer client_data, Atom * selection, |
| Atom * type, XtPointer value, unsigned long *length, |
| int32_t *format) { |
| JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject* pData = (jobject*)client_data; |
| SelectionStatus status = SelectionFailure; |
| |
| if (*type == XT_CONVERT_FAIL) { |
| status = SelectionOwnerTimedOut; |
| } else if (*type != None) { |
| if ((*env)->EnsureLocalCapacity(env, 1) >= 0) { |
| jsize size = (*length <= INT_MAX) ? *length : INT_MAX; |
| jbyteArray array = (*env)->NewByteArray(env, size); |
| |
| if (!JNU_IsNull(env, array)) { |
| (*env)->SetByteArrayRegion(env, array, 0, size, (jbyte*)value); |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } else { |
| *pData = (*env)->NewGlobalRef(env, array); |
| status = SelectionSuccess; |
| } |
| |
| (*env)->DeleteLocalRef(env, array); |
| } |
| } |
| } |
| |
| if (value != NULL) { |
| XtFree(value); |
| value = NULL; |
| } |
| |
| set_selection_status(status); |
| } |
| |
| static int32_t |
| wait_for_selection_event(void *data) { |
| process_convert_data_requests(); |
| return get_selection_status() != SelectionPending; |
| } |
| |
| jlongArray |
| get_selection_targets(JNIEnv *env, Atom selection, Time time_stamp) { |
| jlongArray ret = NULL; |
| jlongArray targets = NULL; |
| SelectionStatus status = SelectionPending; |
| |
| AWT_LOCK(); |
| |
| XtAppSetSelectionTimeout(awt_appContext, |
| JNU_CallStaticMethodByName(env, NULL, "sun/awt/UNIXToolkit", |
| "getDatatransferTimeout", "()I").i); |
| |
| set_selection_status(SelectionPending); |
| XtGetSelectionValue(awt_root_shell, selection, XA_TARGETS, |
| get_selection_targets_callback, (XtPointer)&targets, |
| time_stamp); |
| |
| awt_MToolkit_modalWait(wait_for_selection_event, NULL); |
| status = get_selection_status(); |
| |
| AWT_FLUSH_UNLOCK(); |
| |
| if (!JNU_IsNull(env, targets)) { |
| ret = (*env)->NewLocalRef(env, targets); |
| (*env)->DeleteGlobalRef(env, targets); |
| } |
| |
| switch (status) { |
| case SelectionSuccess: |
| break; |
| case SelectionFailure: |
| JNU_ThrowByName(env, "java/lang/IllegalStateException", |
| "Failed to get selection targets"); |
| break; |
| case SelectionOwnerTimedOut: |
| // return an empty array of targets if the selection owner timed out |
| ret = (*env)->NewLongArray(env, 0); |
| break; |
| default: |
| JNU_ThrowByName(env, "java/lang/IllegalStateException", |
| "Unexpected selection status"); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| jbyteArray |
| get_selection_data(JNIEnv *env, Atom selection, Atom target, Time time_stamp) { |
| jbyteArray ret = NULL; |
| jbyteArray data = NULL; |
| SelectionStatus status = SelectionPending; |
| |
| AWT_LOCK(); |
| |
| XtAppSetSelectionTimeout(awt_appContext, |
| JNU_CallStaticMethodByName(env, NULL, "sun/awt/UNIXToolkit", |
| "getDatatransferTimeout", "()I").i); |
| |
| set_selection_status(SelectionPending); |
| XtGetSelectionValue(awt_root_shell, selection, target, |
| get_selection_data_callback, |
| (XtPointer)&data, time_stamp); |
| |
| awt_MToolkit_modalWait(wait_for_selection_event, NULL); |
| status = get_selection_status(); |
| |
| AWT_FLUSH_UNLOCK(); |
| |
| if (!JNU_IsNull(env, data)) { |
| ret = (*env)->NewLocalRef(env, data); |
| (*env)->DeleteGlobalRef(env, data); |
| } |
| |
| switch (status) { |
| case SelectionSuccess: |
| break; |
| case SelectionFailure: |
| JNU_ThrowIOException(env, "Failed to get selection data"); |
| break; |
| case SelectionOwnerTimedOut: |
| JNU_ThrowIOException(env, "Selection owner timed out"); |
| break; |
| default: |
| JNU_ThrowIOException(env, "Unexpected selection status"); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| void |
| awt_cleanupConvertDataContext(JNIEnv *env, Atom selectionAtom) { |
| awt_convertDataCallbackStruct* structPtr = NULL; |
| |
| if (XFindContext(awt_display, selectionAtom, awt_convertDataContext, |
| (XPointer*)&structPtr) == 0 && structPtr != NULL) { |
| |
| (*env)->DeleteGlobalRef(env, structPtr->source); |
| (*env)->DeleteGlobalRef(env, structPtr->transferable); |
| (*env)->DeleteGlobalRef(env, structPtr->formatMap); |
| (*env)->DeleteGlobalRef(env, structPtr->formats); |
| free(structPtr); |
| } |
| /* |
| * Xlib Programming Manual says that it is better to erase |
| * the current entry with XDeleteContext() before XSaveContext(). |
| */ |
| XDeleteContext(awt_display, selectionAtom, awt_convertDataContext); |
| if (XSaveContext(awt_display, selectionAtom, awt_convertDataContext, |
| (XPointer)NULL) == XCNOMEM) { |
| JNU_ThrowInternalError(env, "XError"); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| |
| static Bool exitSecondaryLoop = True; |
| |
| /* |
| * This predicate procedure allows the Toolkit thread to process specific events |
| * while it is blocked waiting for the event dispatch thread to process |
| * a SunDropTargetEvent. We need this to prevent deadlock when the client code |
| * processing SunDropTargetEvent sets or gets the contents of the system |
| * clipboard/selection. In this case the event dispatch thread waits for the |
| * Toolkit thread to process PropertyNotify or SelectionNotify events. |
| */ |
| static Bool |
| secondary_loop_event(Display* dpy, XEvent* event, char* arg) { |
| return (event->type == SelectionNotify || |
| event->type == SelectionClear || |
| event->type == PropertyNotify) ? True : False; |
| } |
| |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_MToolkitThreadBlockedHandler_enter(JNIEnv *env, jobject this) { |
| DASSERT(exitSecondaryLoop && awt_currentThreadIsPrivileged(env)); |
| exitSecondaryLoop = False; |
| while (!exitSecondaryLoop) { |
| XEvent event; |
| while (XCheckIfEvent(awt_display, &event, secondary_loop_event, NULL)) { |
| XtDispatchEvent(&event); |
| } |
| AWT_WAIT(AWT_DND_POLL_INTERVAL); |
| } |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_MToolkitThreadBlockedHandler_exit(JNIEnv *env, jobject this) { |
| DASSERT(!exitSecondaryLoop && !awt_currentThreadIsPrivileged(env)); |
| exitSecondaryLoop = True; |
| AWT_NOTIFY_ALL(); |
| } |