/* * Copyright 1996-2003 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 "awt_DataTransferer.h" #include "java_awt_datatransfer_Transferable.h" #include "java_awt_datatransfer_DataFlavor.h" #include "sun_awt_motif_X11Selection.h" #include "sun_awt_motif_X11Clipboard.h" #include #include #include #include #include /* fieldIDs for X11Selection fields that may be accessed from C */ static struct X11SelectionIDs { jfieldID holder; jfieldID atom; jfieldID contents; jfieldID selections; } x11SelectionIDs; DECLARE_JAVA_CLASS(selectionClazz, "sun/awt/motif/X11Selection") static jobject call_getSelectionsArray(JNIEnv* env) { DECLARE_STATIC_OBJECT_JAVA_METHOD(getSelectionsArray, selectionClazz, "getSelectionsArray", "()[Ljava/lang/Object;") DASSERT(!JNU_IsNull(env, getSelectionsArray)); return (*env)->CallStaticObjectMethod(env, clazz, getSelectionsArray); } static void call_checkChange(JNIEnv* env, jobject jselection, jlongArray targetArray) { DECLARE_VOID_JAVA_METHOD(checkChangeMID, selectionClazz, "checkChange", "([J)V") DASSERT(!JNU_IsNull(env, jselection)); (*env)->CallVoidMethod(env, jselection, checkChangeMID, targetArray); } static jlongArray call_getSelectionAtomsToCheckChange(JNIEnv* env) { DECLARE_STATIC_OBJECT_JAVA_METHOD(getSelectionAtomsToCheckChangeMID, selectionClazz, "getSelectionAtomsToCheckChange", "()[J") return (jlongArray)(*env)->CallStaticObjectMethod(env, get_selectionClazz(env), getSelectionAtomsToCheckChangeMID); } /* * Class: sun_awt_motif_X11Selection * Method: initIDs * Signature: ()V */ /* This function gets called from the static initializer for X11Selection.java to initialize the fieldIDs for fields that may be accessed from C */ JNIEXPORT void JNICALL Java_sun_awt_motif_X11Selection_initIDs (JNIEnv *env, jclass cls) { x11SelectionIDs.holder = (*env)-> GetFieldID(env, cls, "holder","Lsun/awt/motif/X11SelectionHolder;"); x11SelectionIDs.atom = (*env)->GetFieldID(env, cls, "atom", "J"); x11SelectionIDs.contents = (*env)-> GetFieldID(env, cls, "contents", "Ljava/awt/datatransfer/Transferable;"); x11SelectionIDs.selections = (*env)-> GetStaticFieldID(env, cls, "selections", "Ljava/util/Vector;"); } /* * Class: sun_awt_motif_X11Selection * Method: init * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_awt_motif_X11Selection_init (JNIEnv *env, jclass this) { AWT_LOCK(); AWT_UNLOCK(); } static jobject getX11Selection(JNIEnv * env, Atom atom) { jobjectArray selections; jsize selectionCount, i; jobject selection; jobject returnSelection = NULL; selections = (jobjectArray)call_getSelectionsArray(env); if (JNU_IsNull(env, selections)) { return NULL; } selectionCount = (*env)->GetArrayLength(env, selections); for (i = 0; i < selectionCount; i++) { selection = (*env)->GetObjectArrayElement(env, selections, i); if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); break; } if (JNU_IsNull(env, selection)) { break; } if ((*env)->GetLongField(env, selection, x11SelectionIDs.atom) == atom) { returnSelection = selection; } else { (*env)->DeleteLocalRef(env, selection); } } (*env)->DeleteLocalRef(env, selections); return returnSelection; } Boolean awtJNI_isSelectionOwner(JNIEnv * env, char *sel_str) { Atom selection; jobject x11sel; selection = XInternAtom(awt_display, sel_str, False); x11sel = getX11Selection(env, selection); if (!JNU_IsNull(env, x11sel)) { jobject holder; holder = (*env)->GetObjectField(env, x11sel, x11SelectionIDs.holder); if (!JNU_IsNull(env, holder)) { return TRUE; } } return FALSE; } static void losingSelectionOwnership(Widget w, Atom * selection); void awtJNI_notifySelectionLost(JNIEnv * env, char *sel_str) { Atom selection; selection = XInternAtom(awt_display, sel_str, False); losingSelectionOwnership(NULL, &selection); } static void losingSelectionOwnership(Widget w, Atom * selection) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jobject this = getX11Selection(env, *selection); /* * SECURITY: OK to call this on privileged thread - peer does * not call into client code */ JNU_CallMethodByName(env, NULL, this, "lostSelectionOwnership", "()V"); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } /* * Fix for 4692059. * The native context is cleaned up on the event dispatch thread after the * references to the current contents and owner are cleared. */ } /* * Class: sun_awt_motif_X11Selection * Method: pGetSelectionOwnership * Signature: (Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;[JLjava/util/Map;Lsun/awt/motif/X11SelectionHolder;)Z */ JNIEXPORT jboolean JNICALL Java_sun_awt_motif_X11Selection_pGetSelectionOwnership(JNIEnv *env, jobject this, jobject source, jobject transferable, jlongArray formats, jobject formatMap, jobject holder) { Boolean gotit = False; Atom selection = (Atom)(*env)->GetLongField(env, this, x11SelectionIDs.atom); awt_convertDataCallbackStruct* structPtr = NULL; Time time = CurrentTime; AWT_LOCK(); time = awt_util_getCurrentServerTime(); (*env)->SetObjectField(env, this, x11SelectionIDs.holder, NULL); (*env)->SetObjectField(env, this, x11SelectionIDs.contents, NULL); gotit = XtOwnSelection(awt_root_shell, selection, time, awt_convertData, losingSelectionOwnership, NULL); if (gotit) { if (XFindContext(awt_display, selection, 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); memset(structPtr, 0, sizeof(awt_convertDataCallbackStruct)); } else { XDeleteContext(awt_display, selection, awt_convertDataContext); structPtr = calloc(1, sizeof(awt_convertDataCallbackStruct)); if (structPtr == NULL) { XtDisownSelection(awt_root_shell, selection, time); AWT_UNLOCK(); JNU_ThrowOutOfMemoryError(env, ""); return JNI_FALSE; } if (XSaveContext(awt_display, selection, awt_convertDataContext, (XPointer)structPtr) == XCNOMEM) { XtDisownSelection(awt_root_shell, selection, time); free(structPtr); AWT_UNLOCK(); JNU_ThrowInternalError(env, "Failed to save context data for selection."); return JNI_FALSE; } } structPtr->source = (*env)->NewGlobalRef(env, source); structPtr->transferable = (*env)->NewGlobalRef(env, transferable); structPtr->formatMap = (*env)->NewGlobalRef(env, formatMap); structPtr->formats = (*env)->NewGlobalRef(env, formats); if (JNU_IsNull(env, structPtr->source) || JNU_IsNull(env, structPtr->transferable) || JNU_IsNull(env, structPtr->formatMap) || JNU_IsNull(env, structPtr->formats)) { if (!JNU_IsNull(env, structPtr->source)) { (*env)->DeleteGlobalRef(env, structPtr->source); } if (!JNU_IsNull(env, structPtr->transferable)) { (*env)->DeleteGlobalRef(env, structPtr->transferable); } if (!JNU_IsNull(env, structPtr->formatMap)) { (*env)->DeleteGlobalRef(env, structPtr->formatMap); } if (!JNU_IsNull(env, structPtr->formats)) { (*env)->DeleteGlobalRef(env, structPtr->formats); } XtDisownSelection(awt_root_shell, selection, time); XDeleteContext(awt_display, selection, awt_convertDataContext); free(structPtr); AWT_UNLOCK(); JNU_ThrowOutOfMemoryError(env, ""); return JNI_FALSE; } (*env)->SetObjectField(env, this, x11SelectionIDs.holder, holder); (*env)->SetObjectField(env, this, x11SelectionIDs.contents, transferable); } AWT_UNLOCK(); return (gotit ? JNI_TRUE : JNI_FALSE); } /* * Class: sun_awt_motif_X11Selection * Method: clearNativeContext * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_awt_motif_X11Selection_clearNativeContext(JNIEnv *env, jobject this) { Atom selection = (Atom)(*env)->GetLongField(env, this, x11SelectionIDs.atom); AWT_LOCK(); XtDisownSelection(awt_root_shell, selection, CurrentTime); awt_cleanupConvertDataContext(env, selection); AWT_UNLOCK(); } static void getSelectionTargetsToCheckChange(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); size_t count = 0, i = 0, j = 0; jlongArray targetArray = NULL; // Should keep this in sync with getSelectionTargets() so that // this function yields non-null targetArray iff // getSelectionTargets() yields SelectionSuccess. if (*type == XA_TARGETS || *type == XA_ATOM) { targetArray = getSelectionTargetsHelper(env, value, *length); } else if (*type != XT_CONVERT_FAIL) { targetArray = (*env)->NewLongArray(env, 0); } if (value != NULL) { XtFree(value); value = NULL; } { jobject jselection = getX11Selection(env, *selection); call_checkChange(env, jselection, targetArray); if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } (*env)->DeleteLocalRef(env, targetArray); (*env)->DeleteLocalRef(env, jselection); } } static Atom _XA_JAVA_TIME_PROPERTY_ATOM_CHECK_SELECTION_CHANGE_ON_TIMEOUT = 0; static void checkSelectionChangeOnTimeout(XtPointer client_data, XtIntervalId* id) { // We don't call XtGetSelectionValue(..., TARGETS, ..., awt_util_getCurrentServerTime()) // here because awt_util_getCurrentServerTime() may block toolkit therad for a while // whereas the current function is called very often at regular intervals. // Instead we call XtGetSelectionValue(..., XtLastTimestampProcessed(awt_display)) // in the property change event handler wherein we have got an up-to-date timestamp. XChangeProperty(awt_display, XtWindow(awt_root_shell), _XA_JAVA_TIME_PROPERTY_ATOM_CHECK_SELECTION_CHANGE_ON_TIMEOUT, XA_ATOM, 32, PropModeAppend, (unsigned char *)"", 0); XFlush(awt_display); } static unsigned long selectionPollInterval; static void propertyChangeEventHandlerToSelectionCheck (Widget w, XtPointer client_data, XEvent* event, Boolean* continue_to_dispatch) { JNIEnv *env; jlongArray jselectionAtoms; if (event->type != PropertyNotify || event->xproperty.atom != _XA_JAVA_TIME_PROPERTY_ATOM_CHECK_SELECTION_CHANGE_ON_TIMEOUT) { return; } env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jselectionAtoms = call_getSelectionAtomsToCheckChange(env); DASSERT(!JNU_IsNull(env, jselectionAtoms)); if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } else { jsize len = (*env)->GetArrayLength(env, jselectionAtoms); jlong* selectionAtomsNative = (*env)->GetLongArrayElements(env, jselectionAtoms, NULL); if (!JNU_IsNull(env, selectionAtomsNative)) { jsize i = 0; for (i = 0; i < len; i++) { XtGetSelectionValue(awt_root_shell, (Atom)selectionAtomsNative[i], XA_TARGETS, getSelectionTargetsToCheckChange, (XtPointer)NULL, XtLastTimestampProcessed(awt_display)); } (*env)->ReleaseLongArrayElements(env, jselectionAtoms, selectionAtomsNative, JNI_ABORT); } } // Reschedule the timer callback. XtAppAddTimeOut(awt_appContext, selectionPollInterval, checkSelectionChangeOnTimeout, client_data); } static BOOL isClipboardViewerRegistered = FALSE; /* * Class: sun_awt_motif_X11Clipboard * Method: registerClipboardViewer * Signature: (I)V */ JNIEXPORT void JNICALL Java_sun_awt_motif_X11Clipboard_registerClipboardViewer(JNIEnv *env, jobject self, jint pollInterval) { AWT_LOCK(); if (isClipboardViewerRegistered) { AWT_UNLOCK(); return; } if (_XA_JAVA_TIME_PROPERTY_ATOM_CHECK_SELECTION_CHANGE_ON_TIMEOUT == 0) { _XA_JAVA_TIME_PROPERTY_ATOM_CHECK_SELECTION_CHANGE_ON_TIMEOUT = XInternAtom(awt_display, "_SUNW_JAVA_AWT_TIME_CHECK_SELECTION_CHANGE_ON_TIMEOUT", False); } XtAddEventHandler(awt_root_shell, PropertyChangeMask, False, propertyChangeEventHandlerToSelectionCheck, NULL); selectionPollInterval = pollInterval; XtAppAddTimeOut(awt_appContext, selectionPollInterval, checkSelectionChangeOnTimeout, (XtPointer)NULL); isClipboardViewerRegistered = TRUE; AWT_UNLOCK(); } /* * Class: sun_awt_motif_X11Clipboard * Method: unregisterClipboardViewer * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_awt_motif_X11Clipboard_unregisterClipboardViewer(JNIEnv *env, jobject self) { AWT_LOCK(); if (!isClipboardViewerRegistered) { AWT_UNLOCK(); return; } XtRemoveEventHandler(awt_root_shell, PropertyChangeMask, False, propertyChangeEventHandlerToSelectionCheck, NULL); isClipboardViewerRegistered = FALSE; AWT_UNLOCK(); } /* * Class: sun_awt_motif_X11Clipboard * Method: getClipboardFormats * Signature: (J)[J */ JNIEXPORT jlongArray JNICALL Java_sun_awt_motif_X11Clipboard_getClipboardFormats (JNIEnv *env, jclass cls, jlong selectionAtom) { Time time_stamp = awt_util_getCurrentServerTime(); return get_selection_targets(env, selectionAtom, time_stamp); } /* * Class: sun_awt_motif_X11Clipboard * Method: getClipboardData * Signature: (JJ)[B */ JNIEXPORT jbyteArray JNICALL Java_sun_awt_motif_X11Clipboard_getClipboardData (JNIEnv *env, jclass cls, jlong selectionAtom, jlong format) { Time time_stamp = awt_util_getCurrentServerTime(); return get_selection_data(env, selectionAtom, format, time_stamp); }