blob: 5d5b934459f9ae06bccba6ab7dbd0ff3c5197953 [file] [log] [blame]
/*
* 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 <X11/Intrinsic.h>
#include <X11/Xatom.h>
#include <inttypes.h>
#include <jni.h>
#include <jni_util.h>
/* 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);
}