mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-5860: Implement drag-and-drop [WLToolkit]
This commit is contained in:
@@ -173,6 +173,10 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer,
|
||||
return currentA;
|
||||
}
|
||||
|
||||
protected int getDropAction() {
|
||||
return currentDA;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the Transferable associated with the drop
|
||||
*/
|
||||
|
||||
@@ -147,7 +147,7 @@ public final class WLClipboard extends SunClipboard {
|
||||
}
|
||||
|
||||
WLDataSource newOffer = null;
|
||||
newOffer = dataDevice.createDataSourceFromTransferable(getProtocol(), contents);
|
||||
newOffer = new WLDataSource(dataDevice, getProtocol(), contents);
|
||||
|
||||
synchronized (dataLock) {
|
||||
if (ourDataSource != null) {
|
||||
|
||||
@@ -1188,7 +1188,8 @@ public class WLComponentPeer implements ComponentPeer {
|
||||
|
||||
protected native void nativeDisposeFrame(long ptr);
|
||||
|
||||
private native long getWLSurface(long ptr);
|
||||
private static native long getWLSurface(long ptr);
|
||||
private static native WLComponentPeer nativeGetPeerFromWLSurface(long ptr);
|
||||
private native void nativeStartDrag(long serial, long ptr);
|
||||
private native void nativeStartResize(long serial, long ptr, int edges);
|
||||
|
||||
@@ -1219,6 +1220,14 @@ public class WLComponentPeer implements ComponentPeer {
|
||||
return parent == null ? 0 : getNativePtrFor(parent);
|
||||
}
|
||||
|
||||
static long getWLSurfaceForComponent(Component component) {
|
||||
return getWLSurface(getNativePtrFor(component));
|
||||
}
|
||||
|
||||
static WLComponentPeer getPeerFromWLSurface(long wlSurfaceNativePtr) {
|
||||
return nativeGetPeerFromWLSurface(wlSurfaceNativePtr);
|
||||
}
|
||||
|
||||
private final Object state_lock = new Object();
|
||||
/**
|
||||
* This lock object is used to protect instance data from concurrent access
|
||||
|
||||
@@ -30,6 +30,7 @@ import sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -60,8 +61,8 @@ public class WLDataDevice {
|
||||
WLDataDevice(long wlSeatNativePtr) {
|
||||
nativePtr = initNative(wlSeatNativePtr);
|
||||
|
||||
var queueThread = InnocuousThread.newThread("AWT-Wayland-data-transferer-dispatcher", () -> {
|
||||
dispatchDataTransferQueueImpl(nativePtr);
|
||||
var queueThread = InnocuousThread.newThread("AWT-Wayland-data-source-queue-dispatcher", () -> {
|
||||
dispatchDataSourceQueueImpl(nativePtr);
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("data transfer dispatcher exited");
|
||||
}
|
||||
@@ -84,19 +85,21 @@ public class WLDataDevice {
|
||||
|
||||
private native long initNative(long wlSeatNativePtr);
|
||||
private static native boolean isProtocolSupportedImpl(long nativePtr, int protocol);
|
||||
private static native void dispatchDataTransferQueueImpl(long nativePtr);
|
||||
private static native void dispatchDataSourceQueueImpl(long nativePtr);
|
||||
private static native void setSelectionImpl(int protocol, long nativePtr, long dataOfferNativePtr, long serial);
|
||||
|
||||
public WLDataSource createDataSourceFromTransferable(int protocol, Transferable transferable) {
|
||||
return new WLDataSource(nativePtr, protocol, transferable);
|
||||
}
|
||||
private static native void startDragImpl(long nativePtr, long dataOfferNativePtr,
|
||||
long originSurfaceNativePtr, long iconNativePtr, long serial);
|
||||
|
||||
public boolean isProtocolSupported(int protocol) {
|
||||
return isProtocolSupportedImpl(nativePtr, protocol);
|
||||
}
|
||||
|
||||
public void setSelection(int protocol, WLDataSource source, long serial) {
|
||||
setSelectionImpl(protocol, nativePtr, source.getNativePtr(), serial);
|
||||
setSelectionImpl(protocol, nativePtr, (source == null) ? 0 : source.getNativePtr(), serial);
|
||||
}
|
||||
|
||||
public void startDrag(WLDataSource source, long originSurfaceNativePtr, long iconNativePtr, long serial) {
|
||||
startDragImpl(nativePtr, source.getNativePtr(), originSurfaceNativePtr, iconNativePtr, serial);
|
||||
}
|
||||
|
||||
public WLClipboard getSystemClipboard() {
|
||||
@@ -107,29 +110,34 @@ public class WLDataDevice {
|
||||
return primarySelectionClipboard;
|
||||
}
|
||||
|
||||
long getNativePtr() {
|
||||
return nativePtr;
|
||||
}
|
||||
|
||||
static void transferContentsWithType(Transferable contents, String mime, int fd) {
|
||||
Objects.requireNonNull(contents);
|
||||
Objects.requireNonNull(mime);
|
||||
FileDescriptor javaDestFD = new FileDescriptor();
|
||||
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaDestFD, fd);
|
||||
|
||||
WLDataTransferer wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
|
||||
try {
|
||||
try (var out = new FileOutputStream(javaDestFD)) {
|
||||
Objects.requireNonNull(contents);
|
||||
Objects.requireNonNull(mime);
|
||||
|
||||
SortedMap<Long, DataFlavor> formatMap = wlDataTransferer.getFormatsForTransferable(contents, wlDataTransferer.getFlavorTable());
|
||||
WLDataTransferer wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
|
||||
|
||||
long targetFormat = wlDataTransferer.getFormatForNativeAsLong(mime);
|
||||
DataFlavor flavor = formatMap.get(targetFormat);
|
||||
SortedMap<Long, DataFlavor> formatMap = wlDataTransferer.getFormatsForTransferable(contents, wlDataTransferer.getFlavorTable());
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("will write contents (" + contents + ") in format " + mime + " to fd=" + fd);
|
||||
log.fine("data flavor: " + flavor);
|
||||
}
|
||||
long targetFormat = wlDataTransferer.getFormatForNativeAsLong(mime);
|
||||
DataFlavor flavor = formatMap.get(targetFormat);
|
||||
|
||||
if (flavor != null) {
|
||||
try {
|
||||
byte[] bytes = wlDataTransferer.translateTransferable(contents, flavor, targetFormat);
|
||||
if (bytes == null) return;
|
||||
FileDescriptor javaDestFD = new FileDescriptor();
|
||||
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaDestFD, fd);
|
||||
try (var out = new FileOutputStream(javaDestFD)) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("will write contents (" + contents + ") in format " + mime + " to fd=" + fd);
|
||||
log.fine("data flavor: " + flavor);
|
||||
}
|
||||
|
||||
if (flavor != null) {
|
||||
byte[] bytes = wlDataTransferer.translateTransferable(contents, flavor, targetFormat);
|
||||
if (bytes == null) return;
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("about to write " + bytes.length + " bytes to " + out);
|
||||
}
|
||||
@@ -144,9 +152,9 @@ public class WLDataDevice {
|
||||
ch.write(buffer);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warning("failed to write contents (" + contents + ") in format " + mime + " to fd=" + fd);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warning("failed to write contents (" + contents + ") in format " + mime + " to fd=" + fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,27 +224,43 @@ public class WLDataDevice {
|
||||
return result;
|
||||
}
|
||||
|
||||
private WLDataOffer currentDnDOffer = null;
|
||||
public static int waylandActionsToJava(int waylandActions) {
|
||||
int result = 0;
|
||||
if ((waylandActions & DND_COPY) != 0) {
|
||||
result |= DnDConstants.ACTION_COPY;
|
||||
}
|
||||
if ((waylandActions & DND_MOVE) != 0) {
|
||||
result |= DnDConstants.ACTION_MOVE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int javaActionsToWayland(int javaActions) {
|
||||
int result = 0;
|
||||
if ((javaActions & DnDConstants.ACTION_COPY) != 0) {
|
||||
result |= DND_COPY;
|
||||
}
|
||||
if ((javaActions & DnDConstants.ACTION_MOVE) != 0) {
|
||||
result |= DND_MOVE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Event handlers, called from native on the EDT
|
||||
private void handleDnDEnter(WLDataOffer offer, long serial, long surfacePtr, double x, double y) {
|
||||
if (currentDnDOffer != null) {
|
||||
currentDnDOffer.destroy();
|
||||
}
|
||||
currentDnDOffer = offer;
|
||||
WLDropTargetContextPeer.getInstance().handleEnter(offer, serial, surfacePtr, x, y);
|
||||
}
|
||||
|
||||
private void handleDnDLeave() {
|
||||
if (currentDnDOffer != null) {
|
||||
currentDnDOffer.destroy();
|
||||
currentDnDOffer = null;
|
||||
}
|
||||
WLDropTargetContextPeer.getInstance().handleLeave();
|
||||
}
|
||||
|
||||
private void handleDnDMotion(long timestamp, double x, double y) {
|
||||
WLDropTargetContextPeer.getInstance().handleMotion(timestamp, x, y);
|
||||
}
|
||||
|
||||
private void handleDnDDrop() {
|
||||
WLDropTargetContextPeer.getInstance().handleDrop();
|
||||
}
|
||||
|
||||
private void handleSelection(WLDataOffer offer /* nullable */, int protocol) {
|
||||
|
||||
@@ -32,10 +32,16 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WLDataOffer {
|
||||
public interface EventListener {
|
||||
void availableActionsChanged(int actions);
|
||||
void selectedActionChanged(int action);
|
||||
}
|
||||
|
||||
private long nativePtr;
|
||||
private final List<String> mimes = new ArrayList<>();
|
||||
private int sourceActions = -1;
|
||||
private int selectedAction = -1;
|
||||
private int sourceActions = 0;
|
||||
private int selectedAction = 0;
|
||||
private EventListener listener;
|
||||
|
||||
private static native void destroyImpl(long nativePtr);
|
||||
|
||||
@@ -110,8 +116,20 @@ public class WLDataOffer {
|
||||
setDnDActionsImpl(nativePtr, actions, preferredAction);
|
||||
}
|
||||
|
||||
public synchronized void setListener(EventListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public synchronized List<String> getMimes() {
|
||||
return mimes;
|
||||
return new ArrayList<>(mimes);
|
||||
}
|
||||
|
||||
public synchronized int getSourceActions() {
|
||||
return sourceActions;
|
||||
}
|
||||
|
||||
public synchronized int getSelectedAction() {
|
||||
return selectedAction;
|
||||
}
|
||||
|
||||
// Event handlers, called from native code on the data device dispatch thread
|
||||
@@ -121,9 +139,15 @@ public class WLDataOffer {
|
||||
|
||||
private synchronized void handleSourceActions(int actions) {
|
||||
sourceActions = actions;
|
||||
if (this.listener != null) {
|
||||
this.listener.availableActionsChanged(actions);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void handleAction(int action) {
|
||||
selectedAction = action;
|
||||
if (this.listener != null) {
|
||||
this.listener.selectedActionChanged(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ public class WLDataSource {
|
||||
|
||||
private static native void setDnDActionsImpl(long nativePtr, int actions);
|
||||
|
||||
WLDataSource(long dataDeviceNativePtr, int protocol, Transferable data) {
|
||||
WLDataSource(WLDataDevice dataDevice, int protocol, Transferable data) {
|
||||
var wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
|
||||
|
||||
nativePtr = initNative(dataDeviceNativePtr, protocol);
|
||||
nativePtr = initNative(dataDevice.getNativePtr(), protocol);
|
||||
assert nativePtr != 0; // should've already thrown in native
|
||||
this.data = data;
|
||||
|
||||
@@ -96,19 +96,11 @@ public class WLDataSource {
|
||||
destroy();
|
||||
}
|
||||
|
||||
protected void handleTargetAcceptsMime(String mime) {
|
||||
// TODO: drag-and-drop implementation, synchronization
|
||||
}
|
||||
protected void handleTargetAcceptsMime(String mime) {}
|
||||
|
||||
protected void handleDnDDropPerformed() {
|
||||
// TODO: drag-and-drop implementation, synchronization
|
||||
}
|
||||
protected void handleDnDDropPerformed() {}
|
||||
|
||||
protected void handleDnDFinished() {
|
||||
// TODO: drag-and-drop implementation, synchronization
|
||||
}
|
||||
protected void handleDnDFinished() {}
|
||||
|
||||
protected void handleDnDAction(int action) {
|
||||
// TODO: drag-and-drop implementation, synchronization
|
||||
}
|
||||
protected void handleDnDAction(int action) {}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2025 JetBrains s.r.o.
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.dnd.SunDragSourceContextPeer;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.DragGestureEvent;
|
||||
import java.util.Map;
|
||||
|
||||
public class WLDragSourceContextPeer extends SunDragSourceContextPeer {
|
||||
private final WLDataDevice dataDevice;
|
||||
|
||||
private class WLDragSource extends WLDataSource {
|
||||
private int action;
|
||||
private String mime;
|
||||
private boolean didSendFinishedEvent = false;
|
||||
private boolean didSucceed = false;
|
||||
|
||||
WLDragSource(Transferable data) {
|
||||
super(dataDevice, WLDataDevice.DATA_TRANSFER_PROTOCOL_WAYLAND, data);
|
||||
}
|
||||
|
||||
private void sendFinishedEvent() {
|
||||
if (didSendFinishedEvent) {
|
||||
return;
|
||||
}
|
||||
didSendFinishedEvent = true;
|
||||
|
||||
final int javaAction = didSucceed ? WLDataDevice.waylandActionsToJava(action) : 0;
|
||||
final int x = WLToolkit.getInputState().getPointerX();
|
||||
final int y = WLToolkit.getInputState().getPointerY();
|
||||
WLDragSourceContextPeer.this.dragDropFinished(didSucceed, javaAction, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void handleDnDAction(int action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void handleDnDDropPerformed() {
|
||||
didSucceed = action != 0 && mime != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void handleDnDFinished() {
|
||||
sendFinishedEvent();
|
||||
destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void handleTargetAcceptsMime(String mime) {
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCancelled() {
|
||||
sendFinishedEvent();
|
||||
super.handleCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
public WLDragSourceContextPeer(DragGestureEvent dge, WLDataDevice dataDevice) {
|
||||
super(dge);
|
||||
this.dataDevice = dataDevice;
|
||||
}
|
||||
|
||||
private long getComponentWlSurfacePtr() {
|
||||
var comp = getComponent();
|
||||
while (comp != null) {
|
||||
var peer = AWTAccessor.getComponentAccessor().getPeer(comp);
|
||||
if (peer instanceof WLComponentPeer) {
|
||||
return WLComponentPeer.getWLSurfaceForComponent(comp);
|
||||
}
|
||||
comp = comp.getParent();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startDrag(Transferable trans, long[] formats, Map<Long, DataFlavor> formatMap) {
|
||||
// formats and formatMap are unused, because WLDataSource already references the same DataTransferer singleton
|
||||
var source = new WLDragSource(trans);
|
||||
|
||||
var actions = getDragSourceContext().getSourceActions();
|
||||
int waylandActions = WLDataDevice.javaActionsToWayland(actions);
|
||||
|
||||
source.setDnDActions(waylandActions);
|
||||
|
||||
long eventSerial = WLToolkit.getInputState().pointerButtonSerial();
|
||||
|
||||
var wlSurface = getComponentWlSurfacePtr();
|
||||
dataDevice.startDrag(source, wlSurface, 0, eventSerial);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) {
|
||||
// TODO: setting cursor here doesn't seem to be required on Wayland?
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2025 JetBrains s.r.o.
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.dnd.SunDropTargetContextPeer;
|
||||
import sun.awt.dnd.SunDropTargetEvent;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WLDropTargetContextPeer extends SunDropTargetContextPeer {
|
||||
private WLDataOffer currentOffer;
|
||||
private Component currentTarget;
|
||||
private double currentX;
|
||||
private double currentY;
|
||||
private long[] sourceFormats;
|
||||
private int lastAction = -1;
|
||||
private boolean didDrop = false;
|
||||
|
||||
private WLDropTargetContextPeer() {
|
||||
}
|
||||
|
||||
private static final WLDropTargetContextPeer INSTANCE = new WLDropTargetContextPeer();
|
||||
|
||||
public static WLDropTargetContextPeer getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private synchronized boolean hasTarget() {
|
||||
var dropTarget = getDropTarget();
|
||||
var context = (dropTarget != null) ? dropTarget.getDropTargetContext() : null;
|
||||
return context != null;
|
||||
}
|
||||
|
||||
private synchronized void postEvent(int event) {
|
||||
if (currentOffer == null || currentTarget == null) {
|
||||
return;
|
||||
}
|
||||
var peer = (WLComponentPeer) AWTAccessor.getComponentAccessor().getPeer(currentTarget);
|
||||
var x = peer.surfaceUnitsToJavaUnits((int) currentX);
|
||||
var y = peer.surfaceUnitsToJavaUnits((int) currentY);
|
||||
// var dropAction = WLDataDevice.waylandActionsToJava(currentOffer.getSelectedAction());
|
||||
var actions = WLDataDevice.waylandActionsToJava(currentOffer.getSourceActions());
|
||||
int dropAction = 0;
|
||||
if (hasTarget() && event != MouseEvent.MOUSE_EXITED) {
|
||||
if ((actions & DnDConstants.ACTION_MOVE) != 0) {
|
||||
dropAction = DnDConstants.ACTION_MOVE;
|
||||
} else if ((actions & DnDConstants.ACTION_COPY) != 0) {
|
||||
dropAction = DnDConstants.ACTION_COPY;
|
||||
}
|
||||
}
|
||||
|
||||
postDropTargetEvent(
|
||||
currentTarget,
|
||||
x,
|
||||
y,
|
||||
dropAction,
|
||||
actions,
|
||||
sourceFormats,
|
||||
0,
|
||||
event,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getNativeData(long format) throws IOException {
|
||||
var dataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
|
||||
|
||||
synchronized (this) {
|
||||
if (currentOffer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Since one format can be mapped to multiple mimes, we need to iterate over all of them.
|
||||
for (var mime : currentOffer.getMimes()) {
|
||||
if (dataTransferer.getFormatForNativeAsLong(mime) == format) {
|
||||
return currentOffer.receiveData(mime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("Unknown format " + format + ", aka " + dataTransferer.getNativeForFormat(format));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDropDone(boolean success, int dropAction, boolean isLocal) {
|
||||
reset();
|
||||
}
|
||||
|
||||
private synchronized void updateActions() {
|
||||
if (currentOffer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int action = 0;
|
||||
if (hasTarget()) {
|
||||
action = WLDataDevice.javaActionsToWayland(getDropAction());
|
||||
}
|
||||
if (action != lastAction) {
|
||||
currentOffer.setDnDActions(action, action);
|
||||
lastAction = action;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void reset() {
|
||||
if (currentOffer != null) {
|
||||
currentOffer.destroy();
|
||||
}
|
||||
|
||||
currentOffer = null;
|
||||
currentTarget = null;
|
||||
lastAction = -1;
|
||||
didDrop = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void acceptDrag(int dragOperation) {
|
||||
super.acceptDrag(dragOperation);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void rejectDrag() {
|
||||
super.rejectDrag();
|
||||
updateActions();
|
||||
}
|
||||
|
||||
public synchronized void handleEnter(WLDataOffer offer, long serial, long surfacePtr, double x, double y) {
|
||||
var peer = WLComponentPeer.getPeerFromWLSurface(surfacePtr);
|
||||
if (peer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
currentTarget = peer.getTarget();
|
||||
currentX = x;
|
||||
currentY = y;
|
||||
lastAction = -1;
|
||||
currentOffer = offer;
|
||||
|
||||
var mimes = offer.getMimes();
|
||||
var wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
|
||||
long[] formats = new long[mimes.size()];
|
||||
for (int i = 0; i < mimes.size(); ++i) {
|
||||
formats[i] = wlDataTransferer.getFormatForNativeAsLong(mimes.get(i));
|
||||
}
|
||||
sourceFormats = formats;
|
||||
|
||||
postEvent(MouseEvent.MOUSE_ENTERED);
|
||||
|
||||
currentOffer.setListener(new WLDataOffer.EventListener() {
|
||||
@Override
|
||||
public void availableActionsChanged(int actions) {
|
||||
postEvent(MouseEvent.MOUSE_DRAGGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectedActionChanged(int action) {
|
||||
postEvent(MouseEvent.MOUSE_DRAGGED);
|
||||
}
|
||||
});
|
||||
|
||||
updateActions();
|
||||
|
||||
// Accept all formats by default. Rejecting the drop is done by setting supported actions to 0.
|
||||
for (var mime : offer.getMimes()) {
|
||||
offer.accept(serial, mime);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void handleLeave() {
|
||||
if (!didDrop) {
|
||||
postEvent(MouseEvent.MOUSE_EXITED);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void handleMotion(long timestamp, double x, double y) {
|
||||
currentX = x;
|
||||
currentY = y;
|
||||
postEvent(MouseEvent.MOUSE_DRAGGED);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
public synchronized void handleDrop() {
|
||||
if (hasTarget()) {
|
||||
didDrop = true;
|
||||
postEvent(SunDropTargetEvent.MOUSE_DROPPED);
|
||||
} else {
|
||||
postEvent(MouseEvent.MOUSE_EXITED);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2025 JetBrains s.r.o.
|
||||
* 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.dnd.SunDragSourceContextPeer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.dnd.MouseDragGestureRecognizer;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.Serial;
|
||||
|
||||
public class WLMouseDragGestureRecognizer extends MouseDragGestureRecognizer {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -175348046815052547L;
|
||||
|
||||
private int motionThreshold;
|
||||
|
||||
protected WLMouseDragGestureRecognizer(DragSource ds, Component c, int act, DragGestureListener dgl) {
|
||||
super(ds, c, act, dgl);
|
||||
}
|
||||
|
||||
protected WLMouseDragGestureRecognizer(DragSource ds, Component c, int act) {
|
||||
super(ds, c, act);
|
||||
}
|
||||
|
||||
protected WLMouseDragGestureRecognizer(DragSource ds, Component c) {
|
||||
super(ds, c);
|
||||
}
|
||||
|
||||
protected WLMouseDragGestureRecognizer(DragSource ds) {
|
||||
super(ds);
|
||||
}
|
||||
|
||||
private int getDropAction(MouseEvent e) {
|
||||
return SunDragSourceContextPeer.convertModifiersToDropAction(e.getModifiersEx(), getSourceActions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
events.clear();
|
||||
|
||||
if (getDropAction(e) != 0) {
|
||||
motionThreshold = DragSource.getDragThreshold();
|
||||
appendEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
if (events.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getDropAction(e) == 0) {
|
||||
events.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (events.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int dropAction = getDropAction(e);
|
||||
if (dropAction == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var trigger = (MouseEvent)getTriggerEvent();
|
||||
if (trigger.getPoint().distanceSq(e.getPoint()) >= motionThreshold * motionThreshold) {
|
||||
fireDragGestureRecognized(dropAction, trigger.getPoint());
|
||||
} else {
|
||||
appendEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.dnd.DragGestureRecognizer;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.dnd.InvalidDnDOperationException;
|
||||
import java.awt.dnd.MouseDragGestureRecognizer;
|
||||
import java.awt.dnd.peer.DragSourceContextPeer;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
@@ -550,13 +551,11 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
@Override
|
||||
public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Not implemented: WLToolkit.createDragSourceContextPeer()");
|
||||
}
|
||||
return null;
|
||||
return new WLDragSourceContextPeer(dge, dataDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends DragGestureRecognizer> T
|
||||
createDragGestureRecognizer(Class<T> recognizerClass,
|
||||
DragSource ds,
|
||||
@@ -564,9 +563,15 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
int srcActions,
|
||||
DragGestureListener dgl)
|
||||
{
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Not implemented: WLToolkit.createDragGestureRecognizer()");
|
||||
final LightweightFrame f = SunToolkit.getLightweightFrame(c);
|
||||
if (f != null) {
|
||||
return f.createDragGestureRecognizer(recognizerClass, ds, c, srcActions, dgl);
|
||||
}
|
||||
|
||||
if (recognizerClass.isAssignableFrom(WLMouseDragGestureRecognizer.class)) {
|
||||
return (T)new WLMouseDragGestureRecognizer(ds, c, srcActions, dgl);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@ final class WLToolkitThreadBlockedHandler implements
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
throw new UnsupportedOperationException();
|
||||
WLToolkit.awtLock();
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
throw new UnsupportedOperationException();
|
||||
WLToolkit.awtUnlock();
|
||||
}
|
||||
|
||||
public void enter() {
|
||||
|
||||
@@ -621,11 +621,28 @@ Java_sun_awt_wl_WLComponentPeer_nativeDisposeFrame
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_getWLSurface
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
(JNIEnv *env, jclass clazz, jlong ptr)
|
||||
{
|
||||
return ptr_to_jlong(((struct WLFrame*)jlong_to_ptr(ptr))->wl_surface);
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_sun_awt_wl_WLComponentPeer_nativeGetPeerFromWLSurface
|
||||
(JNIEnv *env, jclass clazz, jlong wlSurfacePtr)
|
||||
{
|
||||
struct wl_surface* wl_surface = jlong_to_ptr(wlSurfacePtr);
|
||||
if (wl_surface == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
struct WLFrame* frame = wl_surface_get_user_data(wl_surface);
|
||||
if (frame == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject ref = (*env)->NewLocalRef(env, frame->nativeFramePeer);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
|
||||
return ref;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartDrag
|
||||
(JNIEnv *env, jobject obj, jlong serial, jlong ptr)
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ struct DataDevice
|
||||
// TODO: it's currently never destroyed, because WLDataDevice is never destroyed
|
||||
jobject javaObject;
|
||||
|
||||
struct wl_event_queue *dataTransferQueue;
|
||||
struct wl_event_queue *dataSourceQueue;
|
||||
struct wl_data_device *wlDataDevice;
|
||||
struct zwp_primary_selection_device_v1 *zwpPrimarySelectionDevice;
|
||||
};
|
||||
@@ -346,7 +346,6 @@ DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protoc
|
||||
if (protocol == DATA_TRANSFER_PROTOCOL_WAYLAND) {
|
||||
struct wl_data_offer *wlDataOffer = waylandObject;
|
||||
|
||||
wl_proxy_set_queue((struct wl_proxy *) wlDataOffer, dataDevice->dataTransferQueue);
|
||||
wl_data_offer_add_listener(wlDataOffer, &wlDataOfferListener, offer);
|
||||
|
||||
offer->wlDataOffer = wlDataOffer;
|
||||
@@ -356,7 +355,6 @@ DataOffer_create(struct DataDevice *dataDevice, enum DataTransferProtocol protoc
|
||||
if (protocol == DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION) {
|
||||
struct zwp_primary_selection_offer_v1 *zwpPrimarySelectionOffer = waylandObject;
|
||||
|
||||
wl_proxy_set_queue((struct wl_proxy *) zwpPrimarySelectionOffer, dataDevice->dataTransferQueue);
|
||||
zwp_primary_selection_offer_v1_add_listener(zwpPrimarySelectionOffer, &zwpPrimarySelectionOfferListener, offer);
|
||||
offer->zwpPrimarySelectionOffer = zwpPrimarySelectionOffer;
|
||||
offer->protocol = DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION;
|
||||
@@ -848,8 +846,8 @@ Java_sun_awt_wl_WLDataDevice_initNative(JNIEnv *env, jobject obj, jlong wlSeatPt
|
||||
}
|
||||
}
|
||||
|
||||
dataDevice->dataTransferQueue = wl_display_create_queue(wl_display);
|
||||
if (dataDevice->dataTransferQueue == NULL) {
|
||||
dataDevice->dataSourceQueue = wl_display_create_queue(wl_display);
|
||||
if (dataDevice->dataSourceQueue == NULL) {
|
||||
JNU_ThrowInternalError(env, "Couldn't create an event queue for the data device");
|
||||
goto error_cleanup;
|
||||
}
|
||||
@@ -865,8 +863,8 @@ Java_sun_awt_wl_WLDataDevice_initNative(JNIEnv *env, jobject obj, jlong wlSeatPt
|
||||
return ptr_to_jlong(dataDevice);
|
||||
|
||||
error_cleanup:
|
||||
if (dataDevice->dataTransferQueue != NULL) {
|
||||
wl_event_queue_destroy(dataDevice->dataTransferQueue);
|
||||
if (dataDevice->dataSourceQueue != NULL) {
|
||||
wl_event_queue_destroy(dataDevice->dataSourceQueue);
|
||||
}
|
||||
|
||||
if (dataDevice->zwpPrimarySelectionDevice != NULL) {
|
||||
@@ -905,12 +903,12 @@ Java_sun_awt_wl_WLDataDevice_isProtocolSupportedImpl(JNIEnv *env, jclass clazz,
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLDataDevice_dispatchDataTransferQueueImpl(JNIEnv *env, jclass clazz, jlong nativePtr)
|
||||
Java_sun_awt_wl_WLDataDevice_dispatchDataSourceQueueImpl(JNIEnv *env, jclass clazz, jlong nativePtr)
|
||||
{
|
||||
struct DataDevice *dataDevice = jlong_to_ptr(nativePtr);
|
||||
assert(dataDevice != NULL);
|
||||
|
||||
while (wl_display_dispatch_queue(wl_display, dataDevice->dataTransferQueue) != -1) {
|
||||
while (wl_display_dispatch_queue(wl_display, dataDevice->dataSourceQueue) != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,6 +934,21 @@ Java_sun_awt_wl_WLDataDevice_setSelectionImpl(JNIEnv *env,
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLDataDevice_startDragImpl(JNIEnv *env, jclass clazz, jlong dataDeviceNativePtr,
|
||||
jlong dataSourceNativePtr, jlong wlSurfacePtr,
|
||||
jlong iconPtr, jlong serial)
|
||||
{
|
||||
struct DataDevice *dataDevice = jlong_to_ptr(dataDeviceNativePtr);
|
||||
assert(dataDevice != NULL);
|
||||
|
||||
struct DataSource *source = jlong_to_ptr(dataSourceNativePtr);
|
||||
assert(source != NULL);
|
||||
|
||||
wl_data_device_start_drag(dataDevice->wlDataDevice, source->wlDataSource, jlong_to_ptr(wlSurfacePtr),
|
||||
jlong_to_ptr(iconPtr), serial);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_awt_wl_WLDataSource_initNative(JNIEnv *env, jobject javaObject, jlong dataDeviceNativePtr, jint protocol)
|
||||
{
|
||||
@@ -973,7 +986,7 @@ Java_sun_awt_wl_WLDataSource_initNative(JNIEnv *env, jobject javaObject, jlong d
|
||||
return 0;
|
||||
}
|
||||
|
||||
wl_proxy_set_queue((struct wl_proxy *) wlDataSource, dataDevice->dataTransferQueue);
|
||||
wl_proxy_set_queue((struct wl_proxy *) wlDataSource, dataDevice->dataSourceQueue);
|
||||
wl_data_source_add_listener(wlDataSource, &wl_data_source_listener, dataSource);
|
||||
|
||||
dataSource->protocol = DATA_TRANSFER_PROTOCOL_WAYLAND;
|
||||
@@ -989,7 +1002,7 @@ Java_sun_awt_wl_WLDataSource_initNative(JNIEnv *env, jobject javaObject, jlong d
|
||||
return 0;
|
||||
}
|
||||
|
||||
wl_proxy_set_queue((struct wl_proxy *) zwpPrimarySelectionSource, dataDevice->dataTransferQueue);
|
||||
wl_proxy_set_queue((struct wl_proxy *) zwpPrimarySelectionSource, dataDevice->dataSourceQueue);
|
||||
zwp_primary_selection_source_v1_add_listener(zwpPrimarySelectionSource,
|
||||
&zwp_primary_selection_source_v1_listener, dataSource);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user