JBR-5860: Implement drag-and-drop [WLToolkit]

This commit is contained in:
Nikita Tsarev
2025-06-06 16:51:01 +02:00
parent 4b17fcc46e
commit 189907d2ae
13 changed files with 625 additions and 75 deletions

View File

@@ -173,6 +173,10 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer,
return currentA;
}
protected int getDropAction() {
return currentDA;
}
/**
* get the Transferable associated with the drop
*/

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);
}
}
}

View File

@@ -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) {}
}

View File

@@ -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?
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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() {

View File

@@ -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)
{

View File

@@ -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);