Skip to content

Commit

Permalink
JBR-7504 WLToolkit - Middle click paste doesn't work properly when pa…
Browse files Browse the repository at this point in the history
…sting to other applications
  • Loading branch information
mkartashev committed Oct 4, 2024
1 parent cb2eb9b commit 231126c
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 30 deletions.
30 changes: 22 additions & 8 deletions src/java.desktop/unix/classes/sun/awt/wl/WLClipboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public final class WLClipboard extends SunClipboard {

// A handle to the native clipboard representation, 0 if not available.
private long clipboardNativePtr; // guarded by 'this'
private long ourOfferNativePtr; // guarded by 'this'

// The list of numeric format IDs the current clipboard is available in;
// could be null or empty.
Expand Down Expand Up @@ -161,7 +162,12 @@ protected void setContentsNative(Transferable contents) {
log.fine("Clipboard: Offering new contents (" + contents + ") in these MIME formats: " + Arrays.toString(mime));
}

offerData(eventSerial, mime, contents, dataOfferQueuePtr);
synchronized (this) {
if (ourOfferNativePtr != 0) {
cancelOffer(ourOfferNativePtr);
}
ourOfferNativePtr = offerData(eventSerial, mime, contents, dataOfferQueuePtr);
}

// Once we have offered the data, someone may come back and ask to provide them.
// In that event, the transferContentsWithType() will be called from native on the
Expand Down Expand Up @@ -303,31 +309,39 @@ private void handleClipboardFormat(long nativePtr, String mime) {
* has been made available. The list of supported formats
* should have already been received and saved in newClipboardFormats.
*
* @param nativePtr a native handle to the clipboard
* @param newClipboardNativePtr a native handle to the clipboard
*/
private void handleNewClipboard(long nativePtr) {
private void handleNewClipboard(long newClipboardNativePtr) {
// Since we have a new clipboard, the existing one is no longer valid.
// We have now way of knowing whether this "new" one is the same as the "old" one.
// We have no way of knowing whether this "new" one is the same as the "old" one.
lostOwnershipNow(null);

if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: new clipboard is available: " + nativePtr);
log.fine("Clipboard: new clipboard is available: " + newClipboardNativePtr);
}

synchronized (this) {
long oldClipboardNativePtr = clipboardNativePtr;
if (oldClipboardNativePtr != 0) {
// "The client must destroy the previous selection data_offer, if any, upon receiving this event."
destroyClipboard(oldClipboardNativePtr);
}
clipboardFormats = newClipboardFormats;
clipboardNativePtr = nativePtr; // Could be NULL
clipboardNativePtr = newClipboardNativePtr; // Could be NULL

newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT);

notifyOfNewFormats(getClipboardFormats());
}
}

private void handleOfferCancelled(long offerNativePtr) {
synchronized (this) {
assert offerNativePtr == ourOfferNativePtr;
ourOfferNativePtr = 0;
}
}

@Override
protected void registerClipboardViewerChecked() {
// TODO: is there any need to do more here?
Expand Down Expand Up @@ -416,8 +430,8 @@ private byte[] readAllBytesFrom(FileInputStream inputStream) throws IOException
private static native long createDataOfferQueue();
private static native void dispatchDataOfferQueueImpl(long dataOfferQueuePtr);
private native long initNative(boolean isPrimary);
private native void offerData(long eventSerial, String[] mime, Object data, long dataOfferQueuePtr);
private native void cancelOffer(long eventSerial); // TODO: this is unused, delete, maybe?
private native long offerData(long eventSerial, String[] mime, Object data, long dataOfferQueuePtr);
private native void cancelOffer(long offerNativePtr);

private native int requestDataInFormat(long clipboardNativePtr, String mime);
private native void destroyClipboard(long clipboardNativePtr);
Expand Down
70 changes: 48 additions & 22 deletions src/java.desktop/unix/native/libawt_wlawt/WLClipboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,22 @@ typedef void* data_source_t;
static jmethodID transferContentsWithTypeMID; // WLCipboard.transferContentsWithType()
static jmethodID handleClipboardFormatMID; // WLCipboard.handleClipboardFormat()
static jmethodID handleNewClipboardMID; // WLCipboard.handleNewClipboard()
static jmethodID handleOfferCancelledMID; // WLCipboard.handleOfferCancelled()
static jfieldID isPrimaryFID; // WLClipboard.isPrimary

typedef struct DataSourcePayload {
data_source_t source;
jobject clipboard; // a global reference to WLClipboard
jobject content; // a global reference to Transferable
jboolean isPrimary;
} DataSourcePayload;

static DataSourcePayload *
DataSourcePayload_Create(jobject clipboard, jobject content)
{
DataSourcePayload * payload = malloc(sizeof(struct DataSourcePayload));
if (payload) {
payload->source = NULL;
payload->clipboard = clipboard;
payload->content = content;
}
Expand Down Expand Up @@ -273,11 +277,25 @@ CleanupClipboard(DataSourcePayload *payload)

if (payload->clipboard != NULL) (*env)->DeleteGlobalRef(env, payload->clipboard);
if (payload->content != NULL) (*env)->DeleteGlobalRef(env, payload->content);

if (payload->source != NULL) {
if (payload->isPrimary) {
zwp_primary_selection_source_v1_destroy(payload->source);
} else {
wl_data_source_destroy(payload->source);
}
}
DataSourcePayload_Destroy(payload);
}
}

static void
OfferCancelled(DataSourcePayload *payload) {
JNIEnv *env = getEnv();
(*env)->CallVoidMethod(env, payload->clipboard, handleOfferCancelledMID, ptr_to_jlong(payload));
EXCEPTION_CLEAR(env);
CleanupClipboard(payload);
}

static void
wl_data_source_target(void *data,
struct wl_data_source *wl_data_source,
Expand All @@ -302,8 +320,9 @@ wl_data_source_handle_cancelled(
void *data,
struct wl_data_source *source)
{
CleanupClipboard(data);
wl_data_source_destroy(source);
JNU_RUNTIME_ASSERT(getEnv(), data != NULL && source == ((DataSourcePayload*)data)->source, "Unexpected data source cancelled");

OfferCancelled(data);
}

static const struct wl_data_source_listener wl_data_source_listener = {
Expand All @@ -330,8 +349,9 @@ zwp_selection_source_handle_cancelled(
void *data,
struct zwp_primary_selection_source_v1 *source)
{
CleanupClipboard(data);
zwp_primary_selection_source_v1_destroy(source);
JNU_RUNTIME_ASSERT(getEnv(), data != NULL && source == ((DataSourcePayload*)data)->source, "Unexpected selection source cancelled");

OfferCancelled(data);
}

static const struct zwp_primary_selection_source_v1_listener zwp_selection_source_listener = {
Expand Down Expand Up @@ -360,6 +380,12 @@ initJavaRefs(JNIEnv* env, jclass wlClipboardClass)
"(J)V",
JNI_FALSE);

GET_METHOD_RETURN(handleOfferCancelledMID,
wlClipboardClass,
"handleOfferCancelled",
"(J)V",
JNI_FALSE);

GET_FIELD_RETURN(isPrimaryFID,
wlClipboardClass,
"isPrimary",
Expand Down Expand Up @@ -538,6 +564,9 @@ offerData(
: (data_source_t)wl_data_device_manager_create_data_source(wl_ddm);

if (source != NULL) {
payload->source = source;
payload->isPrimary = isPrimary;

wl_proxy_set_queue((struct wl_proxy*)source, jlong_to_ptr(dataOfferQueuePtr));

if (isPrimary) {
Expand Down Expand Up @@ -587,7 +616,7 @@ offerData(
* Retains the reference to clipboard content for the further use when the actual
* clipboard data get requested.
*/
JNIEXPORT void JNICALL
JNIEXPORT jlong JNICALL
Java_sun_awt_wl_WLClipboard_offerData(
JNIEnv *env,
jobject obj,
Expand All @@ -597,46 +626,43 @@ Java_sun_awt_wl_WLClipboard_offerData(
jlong dataOfferQueuePtr)
{
jobject clipboardGlobalRef = (*env)->NewGlobalRef(env, obj); // deleted by ...source_handle_cancelled()
CHECK_NULL(clipboardGlobalRef);
CHECK_NULL_RETURN(clipboardGlobalRef, 0);
jobject contentGlobalRef = (*env)->NewGlobalRef(env, content); // deleted by ...source_handle_cancelled()
CHECK_NULL(contentGlobalRef);
CHECK_NULL_RETURN(contentGlobalRef, 0);

DataSourcePayload * payload = DataSourcePayload_Create(clipboardGlobalRef, contentGlobalRef);
if (payload == NULL) {
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
(*env)->DeleteGlobalRef(env, contentGlobalRef);
}
CHECK_NULL_THROW_OOME(env, payload, "failed to allocate memory for DataSourcePayload");
CHECK_NULL_THROW_OOME_RETURN(env, payload, "failed to allocate memory for DataSourcePayload", 0);

const jboolean isPrimary = isPrimarySelectionClipboard(env, obj);
if (!offerData(env,payload, isPrimary, eventSerial, mimeTypes, dataOfferQueuePtr)) {
if (!offerData(env, payload, isPrimary, eventSerial, mimeTypes, dataOfferQueuePtr)) {
// Failed to create a data source; give up and cleanup.
CleanupClipboard(payload);
}

return ptr_to_jlong(payload);
}

JNIEXPORT void JNICALL
Java_sun_awt_wl_WLClipboard_cancelOffer(
JNIEnv *env,
jobject obj,
jlong eventSerial)
jlong payloadNativePtr)
{
// This should automatically deliver the "cancelled" event where we clean up
// both the previous source and the global reference to the transferable object.
const jboolean isPrimary = isPrimarySelectionClipboard(env, obj);
if (isPrimary) {
zwp_primary_selection_device_v1_set_selection(zwp_selection_device, NULL, eventSerial);
} else {
wl_data_device_set_selection(wl_data_device, NULL, eventSerial);
}
wlFlushToServer(env);
JNU_RUNTIME_ASSERT(env, payloadNativePtr != 0, "NULL pointer to clipboard data source");

CleanupClipboard(jlong_to_ptr(payloadNativePtr));
//wlFlushToServer(env);
}

/**
* Asks Wayland to provide the data for the clipboard in the given MIME
* format.
*
* Returns the file desriptor from which the data must be read or -1
* Returns the file descriptor from which the data must be read or -1
* in case of an error.
*
* NB: the returned file descriptor must be closed by the caller.
Expand Down Expand Up @@ -696,4 +722,4 @@ Java_sun_awt_wl_WLClipboard_destroyClipboard(
struct wl_data_offer * offer = jlong_to_ptr(clipboardNativePtr);
wl_data_offer_destroy(offer);
}
}
}

0 comments on commit 231126c

Please sign in to comment.