mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-5857 Wayland: implement clipboard support
This commit is contained in:
418
src/java.desktop/unix/classes/sun/awt/wl/WLClipboard.java
Normal file
418
src/java.desktop/unix/classes/sun/awt/wl/WLClipboard.java
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright 2023 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 jdk.internal.misc.InnocuousThread;
|
||||
import sun.awt.datatransfer.DataTransferer;
|
||||
import sun.awt.datatransfer.SunClipboard;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.FlavorTable;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedMap;
|
||||
|
||||
public final class WLClipboard extends SunClipboard {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLClipboard");
|
||||
|
||||
public static final int INITIAL_MIME_FORMATS_COUNT = 10;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 4096;
|
||||
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
// A native handle of a Wayland queue dedicated to handling
|
||||
// data offer-related events
|
||||
private static final long dataOfferQueuePtr;
|
||||
|
||||
private final long ID;
|
||||
|
||||
// true if this is the "primary selection" clipboard,
|
||||
// false otherwise (the regular clipboard).
|
||||
private final boolean isPrimary; // used by native
|
||||
|
||||
// A handle to the native clipboard representation, 0 if not available.
|
||||
private long clipboardNativePtr; // guarded by 'this'
|
||||
|
||||
// The list of numeric format IDs the current clipboard is available in;
|
||||
// could be null or empty.
|
||||
private List<Long> clipboardFormats; // guarded by 'this'
|
||||
|
||||
// The "current" list formats for the new clipboard contents that is about
|
||||
// to be received from Wayland. Could be empty, but never null.
|
||||
private List<Long> newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT); // guarded by 'this'
|
||||
|
||||
static {
|
||||
initIDs();
|
||||
dataOfferQueuePtr = createDataOfferQueue();
|
||||
flavorTable = DataTransferer.adaptFlavorMap(getDefaultFlavorTable());
|
||||
|
||||
final Thread toolkitSystemThread = InnocuousThread.newThread(
|
||||
"AWT-Wayland-clipboard-dispatcher",
|
||||
WLClipboard::dispatchDataOfferQueue);
|
||||
toolkitSystemThread.setDaemon(true);
|
||||
toolkitSystemThread.start();
|
||||
}
|
||||
|
||||
private final static FlavorTable flavorTable;
|
||||
|
||||
public WLClipboard(String name, boolean isPrimary) {
|
||||
super(name);
|
||||
this.ID = initNative(isPrimary);
|
||||
this.isPrimary = isPrimary;
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: Created " + this);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dispatchDataOfferQueue() {
|
||||
dispatchDataOfferQueueImpl(dataOfferQueuePtr); // does not return until error or server disconnect
|
||||
if (log.isLoggable(PlatformLogger.Level.INFO)) {
|
||||
log.info("Clipboard: data offer dispatcher exited");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Clipboard: Wayland %s (%x)", (isPrimary ? "selection clipboard" : "clipboard"), ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we loose ownership of the clipboard.
|
||||
*/
|
||||
@Override
|
||||
protected void clearNativeContext() {
|
||||
// Unused in the Wayland clipboard as we don't (and can't) keep
|
||||
// any references to the native clipboard once we have lost keyboard focus.
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: Lost ownership of our clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to make the new clipboard contents known to Wayland.
|
||||
*
|
||||
* @param contents clipboard's contents.
|
||||
*/
|
||||
@Override
|
||||
protected void setContentsNative(Transferable contents) {
|
||||
// The server requires "serial number of the event that triggered this request"
|
||||
// as a proof of the right to copy data.
|
||||
WLPointerEvent wlPointerEvent = WLToolkit.getInputState().eventWithSerial();
|
||||
long eventSerial = wlPointerEvent == null ? 0 : wlPointerEvent.getSerial();
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: About to offer new contents using Wayland event serial " + eventSerial);
|
||||
}
|
||||
if (eventSerial != 0) {
|
||||
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
|
||||
long[] formats = wlDataTransferer.getFormatsForTransferableAsArray(contents, flavorTable);
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: New one is available in these integer formats: " + Arrays.toString(formats));
|
||||
}
|
||||
notifyOfNewFormats(formats);
|
||||
|
||||
if (formats.length > 0) {
|
||||
String[] mime = new String[formats.length];
|
||||
for (int i = 0; i < formats.length; i++) {
|
||||
mime[i] = wlDataTransferer.getNativeForFormat(formats[i]);
|
||||
}
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: Offering new contents (" + contents + ") in these MIME formats: " + Arrays.toString(mime));
|
||||
}
|
||||
|
||||
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 EDT.
|
||||
// A reference to contents is retained until we are notified of the new contents
|
||||
// by the Wayland server.
|
||||
}
|
||||
} else {
|
||||
this.owner = null;
|
||||
this.contents = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from native on EDT when a client has asked to provide the actual data for
|
||||
* the clipboard that we own in the given format to the given file.
|
||||
* NB: that client could be us, but we aren't necessarily aware of that once we
|
||||
* lost keyboard focus at least once after Ctrl-C.
|
||||
*
|
||||
* @param contents a reference to the clipboard's contents to be transferred
|
||||
* @param mime transfer the contents in this MIME format
|
||||
* @param destFD transfer the contents to this file descriptor and close it afterward
|
||||
*
|
||||
* @throws IOException in case writing to the given file descriptor failed
|
||||
*/
|
||||
private void transferContentsWithType(Transferable contents, String mime, int destFD) throws IOException {
|
||||
assert SwingUtilities.isEventDispatchThread();
|
||||
Objects.requireNonNull(contents);
|
||||
Objects.requireNonNull(mime);
|
||||
|
||||
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
|
||||
SortedMap<Long,DataFlavor> formatMap =
|
||||
wlDataTransferer.getFormatsForTransferable(contents, flavorTable);
|
||||
|
||||
long targetFormat = wlDataTransferer.getFormatForNativeAsLong(mime);
|
||||
DataFlavor flavor = formatMap.get(targetFormat);
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: will write contents (" + contents + ") in format " + mime + " to fd=" + destFD);
|
||||
log.fine("Clipboard: data flavor: " + flavor);
|
||||
}
|
||||
|
||||
if (flavor != null) {
|
||||
FileDescriptor javaDestFD = new FileDescriptor();
|
||||
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaDestFD, destFD);
|
||||
|
||||
try (var out = new FileOutputStream(javaDestFD)) {
|
||||
byte[] bytes = wlDataTransferer.translateTransferable(contents, flavor, targetFormat);
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: about to write " + (bytes != null ? bytes.length : 0) + " bytes to " + out);
|
||||
}
|
||||
// TODO: large data transfer will block EDT for a long time.
|
||||
// Implement an option to do the writing on a dedicated thread.
|
||||
// Alternatively, arrange for this event to arrive on a dedicated queue and
|
||||
// only work with this queue on a dedicated thread.
|
||||
out.write(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return formats the current clipboard is available in; could be null
|
||||
*/
|
||||
@Override
|
||||
protected long[] getClipboardFormats() {
|
||||
synchronized (this) {
|
||||
if (clipboardFormats != null && !clipboardFormats.isEmpty()) {
|
||||
long[] res = new long[clipboardFormats.size()];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
res[i] = clipboardFormats.get(i);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The clipboard contents in the given numeric format ID.
|
||||
*
|
||||
* @param format the numeric ID of the format to provide clipboard contents in
|
||||
* @return contents in the given numeric format ID
|
||||
* @throws IOException when reading from the clipboard file fails
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getClipboardData(long format) throws IOException {
|
||||
synchronized (this) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: requested content of clipboard with handle "
|
||||
+ clipboardNativePtr + " in format " + format);
|
||||
}
|
||||
if (clipboardNativePtr != 0) {
|
||||
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
|
||||
String mime = wlDataTransferer.getNativeForFormat(format);
|
||||
int fd = requestDataInFormat(clipboardNativePtr, mime);
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: will read data from " + fd + " in format " + mime);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
FileDescriptor javaFD = new FileDescriptor();
|
||||
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaFD, fd);
|
||||
try (var in = new FileInputStream(javaFD)) {
|
||||
byte[] bytes = readAllBytesFrom(in);
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: read data from " + fd + ": "
|
||||
+ (bytes != null ? bytes.length : 0) + " bytes");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from native to notify us of the availability of a new clipboard
|
||||
* denoted by the native handle in a specific MIME format.
|
||||
* This method is usually called repeatedly with the same nativePtr and
|
||||
* different formats. When all formats are announces in this way,
|
||||
* handleNewClipboard() is called.
|
||||
*
|
||||
* @param nativePtr a native handle to the clipboard
|
||||
* @param mime the MIME format in which this clipboard is available.
|
||||
*/
|
||||
private void handleClipboardFormat(long nativePtr, String mime) {
|
||||
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
|
||||
Long format = wlDataTransferer.getFormatForNativeAsLong(mime);
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: new format is available for " + nativePtr + ": " + mime);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
newClipboardFormats.add(format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from native to notify us that a new clipboard content
|
||||
* 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
|
||||
*/
|
||||
private void handleNewClipboard(long nativePtr) {
|
||||
// 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.
|
||||
lostOwnershipNow(null);
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: new clipboard is available: " + nativePtr);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
long oldClipboardNativePtr = clipboardNativePtr;
|
||||
if (oldClipboardNativePtr != 0) {
|
||||
destroyClipboard(oldClipboardNativePtr);
|
||||
}
|
||||
clipboardFormats = newClipboardFormats;
|
||||
clipboardNativePtr = nativePtr; // Could be NULL
|
||||
|
||||
newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT);
|
||||
|
||||
notifyOfNewFormats(getClipboardFormats());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerClipboardViewerChecked() {
|
||||
// TODO: is there any need to do more here?
|
||||
log.info("Unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterClipboardViewerChecked() {
|
||||
log.info("Unimplemented");
|
||||
}
|
||||
|
||||
private void notifyOfNewFormats(long[] formats) {
|
||||
if (areFlavorListenersRegistered()) {
|
||||
checkChange(formats);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given input stream until EOF and returns its contents as an array of bytes.
|
||||
*/
|
||||
private byte[] readAllBytesFrom(FileInputStream inputStream) throws IOException {
|
||||
int len = Integer.MAX_VALUE;
|
||||
List<byte[]> bufs = null;
|
||||
byte[] result = null;
|
||||
int total = 0;
|
||||
int remaining = len;
|
||||
int n;
|
||||
do {
|
||||
byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
|
||||
int nread = 0;
|
||||
|
||||
while ((n = inputStream.read(buf, nread,
|
||||
Math.min(buf.length - nread, remaining))) > 0) {
|
||||
nread += n;
|
||||
remaining -= n;
|
||||
}
|
||||
|
||||
if (nread > 0) {
|
||||
if (MAX_BUFFER_SIZE - total < nread) {
|
||||
throw new OutOfMemoryError("Required array size too large");
|
||||
}
|
||||
if (nread < buf.length) {
|
||||
buf = Arrays.copyOfRange(buf, 0, nread);
|
||||
}
|
||||
total += nread;
|
||||
if (result == null) {
|
||||
result = buf;
|
||||
} else {
|
||||
if (bufs == null) {
|
||||
bufs = new ArrayList<>();
|
||||
bufs.add(result);
|
||||
}
|
||||
bufs.add(buf);
|
||||
}
|
||||
}
|
||||
// if the last call to read returned -1 or the number of bytes
|
||||
// requested have been read then break
|
||||
} while (n >= 0 && remaining > 0);
|
||||
|
||||
if (bufs == null) {
|
||||
if (result == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
return result.length == total ?
|
||||
result : Arrays.copyOf(result, total);
|
||||
}
|
||||
|
||||
result = new byte[total];
|
||||
int offset = 0;
|
||||
remaining = total;
|
||||
for (byte[] b : bufs) {
|
||||
int count = Math.min(b.length, remaining);
|
||||
System.arraycopy(b, 0, result, offset, count);
|
||||
offset += count;
|
||||
remaining -= count;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static native void initIDs();
|
||||
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 int requestDataInFormat(long clipboardNativePtr, String mime);
|
||||
private native void destroyClipboard(long clipboardNativePtr);
|
||||
}
|
||||
357
src/java.desktop/unix/classes/sun/awt/wl/WLDataTransferer.java
Normal file
357
src/java.desktop/unix/classes/sun/awt/wl/WLDataTransferer.java
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright 2023 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.datatransfer.DataTransferer;
|
||||
import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
|
||||
import sun.datatransfer.DataFlavorUtil;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Facilitates data conversion between formats for the use with the clipboard.
|
||||
* Some terminology:
|
||||
* "native" format - the format understood by Wayland; MIME with some deviations
|
||||
* "long" format - an arbintrary number assigned to some native format;
|
||||
* once established, this mapping never changes
|
||||
*/
|
||||
public class WLDataTransferer extends DataTransferer {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLDataTransferer");
|
||||
|
||||
private static ImageTypeSpecifier defaultImageSpec = null;
|
||||
|
||||
// Maps the "native" format (MIME) to its numeric ID
|
||||
private final Map<String, Long> nameToLong = new HashMap<>();
|
||||
|
||||
// Maps the numeric ID of a format to its native (MIME) representation
|
||||
private final Map<Long, String> longToName = new HashMap<>();
|
||||
|
||||
private final Map<Long, Boolean> imageFormats = new HashMap<>();
|
||||
private final Map<Long, Boolean> textFormats = new HashMap<>();
|
||||
|
||||
private static class HOLDER {
|
||||
static WLDataTransferer instance = new WLDataTransferer();
|
||||
}
|
||||
|
||||
static WLDataTransferer getInstanceImpl() {
|
||||
return HOLDER.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultUnicodeEncoding() {
|
||||
return "UTF-8";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocaleDependentTextFormat(long format) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFileFormat(long format) {
|
||||
String nat = getNativeForFormat(format);
|
||||
return "FILE_NAME".equals(nat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImageFormat(long format) {
|
||||
synchronized (this) {
|
||||
return imageFormats.computeIfAbsent(format, f -> isMimeFormat(f, "image"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTextFormat(long format) {
|
||||
synchronized (this) {
|
||||
return textFormats.computeIfAbsent(
|
||||
format,
|
||||
f -> super.isTextFormat(format)
|
||||
|| isMimeFormat(format, "text"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the given format ID corresponds to a MIME format
|
||||
* with the given primary type
|
||||
*/
|
||||
private boolean isMimeFormat(long format, String primaryType) {
|
||||
String nat = getNativeForFormat(format);
|
||||
if (nat != null) {
|
||||
try {
|
||||
DataFlavor df = new DataFlavor(nat);
|
||||
if (primaryType.equals(df.getPrimaryType())) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception ignored) { /* Not MIME */ }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getFormatForNativeAsLong(String formatName) {
|
||||
Objects.requireNonNull(formatName);
|
||||
synchronized (this) {
|
||||
Long thisID = nameToLong.get(formatName);
|
||||
if (thisID == null) {
|
||||
// Some apps request data in a format that only differs from
|
||||
// the advertised in the case of some of the letters.
|
||||
// IMO we can ignore the case and find an equivalent.
|
||||
var matchingKey = nameToLong.keySet().stream()
|
||||
.filter(formatName::equalsIgnoreCase).findAny()
|
||||
.orElse(null);
|
||||
if (matchingKey != null) {
|
||||
thisID = nameToLong.get(matchingKey);
|
||||
} else {
|
||||
long nextID = nameToLong.size();
|
||||
thisID = nextID;
|
||||
longToName.put(thisID, formatName);
|
||||
}
|
||||
nameToLong.put(formatName, thisID);
|
||||
}
|
||||
|
||||
return thisID;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNativeForFormat(long format) {
|
||||
synchronized (this) {
|
||||
return longToName.get(format);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
byte[] bytes = fileList.get(i).getBytes();
|
||||
if (i != 0) bos.write(0);
|
||||
bos.write(bytes, 0, bytes.length);
|
||||
}
|
||||
return bos;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] dragQueryFile(byte[] bytes) {
|
||||
// TODO
|
||||
log.info("Unimplemented");
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Image platformImageBytesToImage(byte[] bytes, long format) throws IOException {
|
||||
DataFlavor df = getImageDataFlavorForFormat(format);
|
||||
final String baseType = df.getPrimaryType() + "/" + df.getSubType();
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByMIMEType(baseType);
|
||||
BufferedImage bi = null;
|
||||
if (readers.hasNext()) {
|
||||
ImageReader reader = readers.next();
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(byteStream);
|
||||
reader.setInput(stream, true, true);
|
||||
try (stream) {
|
||||
bi = reader.read(0, param);
|
||||
} finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return bi;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] imageToPlatformBytes(Image image, long format) throws IOException {
|
||||
int width = image.getWidth(null);
|
||||
int height = image.getHeight(null);
|
||||
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = bufferedImage.createGraphics();
|
||||
g2d.drawImage(image, 0, 0, null);
|
||||
g2d.dispose();
|
||||
|
||||
int numBytes = (int)(width * height * bufferedImage.getColorModel().getPixelSize() / 8.0);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(numBytes);
|
||||
DataFlavor df = getImageDataFlavorForFormat(format);
|
||||
ImageIO.write(bufferedImage, df.getSubType(), out);
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private DataFlavor getImageDataFlavorForFormat(long format) {
|
||||
String nat = getNativeForFormat(format);
|
||||
DataFlavor df = null;
|
||||
try {
|
||||
df = new DataFlavor(nat);
|
||||
} catch (Exception ignored) { }
|
||||
|
||||
if (df == null) {
|
||||
throw new InternalError("Native image format " + nat + " corresponding to ID "
|
||||
+ format + " not recognized as DataFlavor");
|
||||
}
|
||||
return df;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {
|
||||
return WLToolkitThreadBlockedHandler.getToolkitThreadBlockedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
|
||||
// TODO: much of this has been taken verbatim from XDataTransferer.
|
||||
// Worth refactoring the common stuff out?
|
||||
LinkedHashSet<DataFlavor> flavors = new LinkedHashSet<>();
|
||||
|
||||
if (nat == null) {
|
||||
return flavors;
|
||||
}
|
||||
|
||||
DataFlavor df;
|
||||
try {
|
||||
df = new DataFlavor(nat);
|
||||
} catch (Exception e) {
|
||||
// The string doesn't constitute a valid MIME type.
|
||||
return flavors;
|
||||
}
|
||||
|
||||
DataFlavor value = df;
|
||||
final String primaryType = df.getPrimaryType();
|
||||
|
||||
// For text formats we map natives to MIME strings instead of data
|
||||
// flavors to enable dynamic text native-to-flavor mapping generation.
|
||||
// See SystemFlavorMap.getFlavorsForNative() for details.
|
||||
if ("image".equals(primaryType)) {
|
||||
final String baseType = primaryType + "/" + df.getSubType();
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByMIMEType(baseType);
|
||||
if (readers.hasNext()) {
|
||||
flavors.add(DataFlavor.imageFlavor);
|
||||
}
|
||||
}
|
||||
|
||||
flavors.add(value);
|
||||
|
||||
return flavors;
|
||||
}
|
||||
|
||||
private ImageTypeSpecifier getDefaultImageTypeSpecifier() {
|
||||
if (defaultImageSpec == null) {
|
||||
ColorModel model = ColorModel.getRGBdefault();
|
||||
WritableRaster raster =
|
||||
model.createCompatibleWritableRaster(10, 10);
|
||||
|
||||
BufferedImage bufferedImage =
|
||||
new BufferedImage(model, raster, model.isAlphaPremultiplied(),
|
||||
null);
|
||||
|
||||
defaultImageSpec = new ImageTypeSpecifier(bufferedImage);
|
||||
}
|
||||
|
||||
return defaultImageSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
|
||||
LinkedHashSet<String> natives = new LinkedHashSet<>(1);
|
||||
|
||||
if (df == null) {
|
||||
return natives;
|
||||
}
|
||||
|
||||
String charset = df.getParameter("charset");
|
||||
String baseType = df.getPrimaryType() + "/" + df.getSubType();
|
||||
String mimeType = baseType;
|
||||
|
||||
if (charset != null && DataFlavorUtil.isFlavorCharsetTextType(df)) {
|
||||
mimeType += ";charset=" + charset;
|
||||
}
|
||||
|
||||
// Add a mapping to the MIME native whenever the representation class
|
||||
// doesn't require translation.
|
||||
if (df.getRepresentationClass() != null &&
|
||||
(df.isRepresentationClassInputStream() ||
|
||||
df.isRepresentationClassByteBuffer() ||
|
||||
byte[].class.equals(df.getRepresentationClass()))) {
|
||||
natives.add(mimeType);
|
||||
}
|
||||
|
||||
if (DataFlavor.imageFlavor.equals(df)) {
|
||||
String[] mimeTypes = ImageIO.getWriterMIMETypes();
|
||||
if (mimeTypes != null) {
|
||||
for (String mime : mimeTypes) {
|
||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByMIMEType(mime);
|
||||
while (writers.hasNext()) {
|
||||
ImageWriter imageWriter = writers.next();
|
||||
ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
|
||||
|
||||
if (writerSpi != null &&
|
||||
writerSpi.canEncodeImage(getDefaultImageTypeSpecifier())) {
|
||||
natives.add(mime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (DataFlavorUtil.isFlavorCharsetTextType(df)) {
|
||||
// stringFlavor is semantically equivalent to the standard
|
||||
// "text/plain" MIME type.
|
||||
if (DataFlavor.stringFlavor.equals(df)) {
|
||||
baseType = "text/plain";
|
||||
}
|
||||
|
||||
for (String encoding : DataFlavorUtil.standardEncodings()) {
|
||||
if (!encoding.equals(charset)) {
|
||||
natives.add(baseType + ";charset=" + encoding);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a MIME format without specified charset.
|
||||
natives.add(baseType);
|
||||
}
|
||||
|
||||
return natives;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ package sun.awt.wl;
|
||||
import jdk.internal.misc.InnocuousThread;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AWTAutoShutdown;
|
||||
import sun.awt.AWTPermissions;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.LightweightFrame;
|
||||
import sun.awt.PeerEvent;
|
||||
@@ -86,6 +87,7 @@ import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -138,6 +140,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
private static final int NUM_LOCK_MASK = 0x02;
|
||||
|
||||
private static boolean initialized = false;
|
||||
private static Thread toolkitThread;
|
||||
private final WLClipboard clipboard;
|
||||
private final WLClipboard selection;
|
||||
|
||||
private static native void initIDs();
|
||||
|
||||
@@ -159,7 +164,7 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
});
|
||||
|
||||
if (!GraphicsEnvironment.isHeadless()) {
|
||||
Thread toolkitThread = InnocuousThread.newThread("AWT-Wayland", this);
|
||||
toolkitThread = InnocuousThread.newThread("AWT-Wayland", this);
|
||||
toolkitThread.setDaemon(true);
|
||||
toolkitThread.start();
|
||||
|
||||
@@ -169,6 +174,19 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
// Wait here for all display sync events to have been received?
|
||||
}
|
||||
|
||||
WLClipboard selectionClipboard = null;
|
||||
try {
|
||||
selectionClipboard = new WLClipboard("Selection", true);
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
|
||||
clipboard = new WLClipboard("System", false);
|
||||
selection = selectionClipboard;
|
||||
}
|
||||
|
||||
public static boolean isToolkitThread() {
|
||||
return Thread.currentThread() == toolkitThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -751,8 +769,7 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
@Override
|
||||
public DataTransferer getDataTransferer() {
|
||||
log.info("Not implemented: WLToolkit.getDataTransferer()");
|
||||
return null;
|
||||
return WLDataTransferer.getInstanceImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -784,14 +801,23 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
@Override
|
||||
public Clipboard getSystemClipboard() {
|
||||
log.info("Not implemented: WLToolkit.getSystemClipboard()");
|
||||
return null;
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
|
||||
}
|
||||
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getSystemSelection() {
|
||||
log.info("Not implemented: WLToolkit.getSystemSelection()");
|
||||
return null;
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2004, Oracle and/or its affiliates. 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. 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.datatransfer.ToolkitThreadBlockedHandler;
|
||||
|
||||
// TODO: this class is essentially unused; not sure if it even has to be here
|
||||
final class WLToolkitThreadBlockedHandler implements
|
||||
ToolkitThreadBlockedHandler {
|
||||
private static final ToolkitThreadBlockedHandler privilegedLock = new WLToolkitThreadBlockedHandler();
|
||||
private static final WLToolkit tk = (WLToolkit) java.awt.Toolkit.getDefaultToolkit();
|
||||
|
||||
private WLToolkitThreadBlockedHandler() {
|
||||
}
|
||||
|
||||
static ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {
|
||||
return privilegedLock;
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void enter() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
138
src/java.desktop/unix/native/libawt_wlawt/JNIUtilities.h
Normal file
138
src/java.desktop/unix/native/libawt_wlawt/JNIUtilities.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef JNIUTILITIES_H_
|
||||
#define JNIUTILITIES_H_
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
#define CHECK_NULL_THROW_OOME(env, x, msg) \
|
||||
do { \
|
||||
if ((x) == NULL) { \
|
||||
JNU_ThrowOutOfMemoryError((env), (msg));\
|
||||
return; \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
#define CHECK_NULL_THROW_OOME_RETURN(env, x, msg, z)\
|
||||
do { \
|
||||
if ((x) == NULL) { \
|
||||
JNU_ThrowOutOfMemoryError((env), (msg));\
|
||||
return (z); \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
#define CHECK_NULL_THROW_IE(env, x, msg) \
|
||||
do { \
|
||||
if ((x) == NULL) { \
|
||||
JNU_ThrowInternalError((env), (msg));\
|
||||
return; \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
/******** GET CLASS SUPPORT *********/
|
||||
|
||||
#define GET_CLASS(dst_var, cls) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->FindClass(env, cls); \
|
||||
if (dst_var != NULL) dst_var = (*env)->NewGlobalRef(env, dst_var); \
|
||||
} \
|
||||
CHECK_NULL(dst_var);
|
||||
|
||||
#define DECLARE_CLASS(dst_var, cls) \
|
||||
static jclass dst_var = NULL; \
|
||||
GET_CLASS(dst_var, cls);
|
||||
|
||||
#define GET_CLASS_RETURN(dst_var, cls, ret) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->FindClass(env, cls); \
|
||||
if (dst_var != NULL) dst_var = (*env)->NewGlobalRef(env, dst_var); \
|
||||
} \
|
||||
CHECK_NULL_RETURN(dst_var, ret);
|
||||
|
||||
#define DECLARE_CLASS_RETURN(dst_var, cls, ret) \
|
||||
static jclass dst_var = NULL; \
|
||||
GET_CLASS_RETURN(dst_var, cls, ret);
|
||||
|
||||
|
||||
/******** GET METHOD SUPPORT *********/
|
||||
|
||||
#define GET_METHOD(dst_var, cls, name, signature) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetMethodID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL(dst_var);
|
||||
|
||||
#define DECLARE_METHOD(dst_var, cls, name, signature) \
|
||||
static jmethodID dst_var = NULL; \
|
||||
GET_METHOD(dst_var, cls, name, signature);
|
||||
|
||||
#define GET_METHOD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetMethodID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL_RETURN(dst_var, ret);
|
||||
|
||||
#define DECLARE_METHOD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
static jmethodID dst_var = NULL; \
|
||||
GET_METHOD_RETURN(dst_var, cls, name, signature, ret);
|
||||
|
||||
#define GET_STATIC_METHOD(dst_var, cls, name, signature) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetStaticMethodID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL(dst_var);
|
||||
|
||||
#define DECLARE_STATIC_METHOD(dst_var, cls, name, signature) \
|
||||
static jmethodID dst_var = NULL; \
|
||||
GET_STATIC_METHOD(dst_var, cls, name, signature);
|
||||
|
||||
#define GET_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetStaticMethodID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL_RETURN(dst_var, ret);
|
||||
|
||||
#define DECLARE_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
static jmethodID dst_var = NULL; \
|
||||
GET_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret);
|
||||
|
||||
/******** GET FIELD SUPPORT *********/
|
||||
|
||||
|
||||
#define GET_FIELD(dst_var, cls, name, signature) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetFieldID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL(dst_var);
|
||||
|
||||
#define DECLARE_FIELD(dst_var, cls, name, signature) \
|
||||
static jfieldID dst_var = NULL; \
|
||||
GET_FIELD(dst_var, cls, name, signature);
|
||||
|
||||
#define GET_FIELD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetFieldID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL_RETURN(dst_var, ret);
|
||||
|
||||
#define DECLARE_FIELD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
static jfieldID dst_var = NULL; \
|
||||
GET_FIELD_RETURN(dst_var, cls, name, signature, ret);
|
||||
|
||||
#define GET_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
if (dst_var == NULL) { \
|
||||
dst_var = (*env)->GetStaticFieldID(env, cls, name, signature); \
|
||||
} \
|
||||
CHECK_NULL_RETURN(dst_var, ret);
|
||||
|
||||
#define DECLARE_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret) \
|
||||
static jfieldID dst_var = NULL; \
|
||||
GET_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret);
|
||||
|
||||
/******** EXCEPTIONS SUPPORT *********/
|
||||
|
||||
#define EXCEPTION_CLEAR(env) \
|
||||
if ((*env)->ExceptionCheck(env)) { \
|
||||
(*env)->ExceptionClear(env); \
|
||||
}
|
||||
|
||||
#endif // JNIUTILITIES_H_
|
||||
677
src/java.desktop/unix/native/libawt_wlawt/WLClipboard.c
Normal file
677
src/java.desktop/unix/native/libawt_wlawt/WLClipboard.c
Normal file
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* Copyright (c) 2023, JetBrains s.r.o.. 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. 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.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "JNIUtilities.h"
|
||||
#include "sun_awt_wl_WLClipboard.h"
|
||||
#include "wayland-client-protocol.h"
|
||||
#include "WLToolkit.h"
|
||||
|
||||
// A type both zwp_primary_selection_source_v1* and wl_data_source*
|
||||
// are convertible to.
|
||||
typedef void* data_source_t;
|
||||
|
||||
static jmethodID transferContentsWithTypeMID; // WLCipboard.transferContentsWithType()
|
||||
static jmethodID handleClipboardFormatMID; // WLCipboard.handleClipboardFormat()
|
||||
static jmethodID handleNewClipboardMID; // WLCipboard.handleNewClipboard()
|
||||
static jfieldID isPrimaryFID; // WLClipboard.isPrimary
|
||||
|
||||
typedef struct DataSourcePayload {
|
||||
jobject clipboard; // a global reference to WLClipboard
|
||||
jobject content; // a global reference to Transferable
|
||||
} DataSourcePayload;
|
||||
|
||||
static DataSourcePayload *
|
||||
DataSourcePayload_Create(jobject clipboard, jobject content)
|
||||
{
|
||||
DataSourcePayload * payload = malloc(sizeof(struct DataSourcePayload));
|
||||
if (payload) {
|
||||
payload->clipboard = clipboard;
|
||||
payload->content = content;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
static void
|
||||
DataSourcePayload_Destroy(DataSourcePayload* payload)
|
||||
{
|
||||
free(payload);
|
||||
}
|
||||
|
||||
typedef struct DataOfferPayload {
|
||||
jobject clipboard; // a global reference to WLClipboard
|
||||
} DataOfferPayload;
|
||||
|
||||
static DataOfferPayload *
|
||||
DataOfferPayload_Create(jobject clipboard)
|
||||
{
|
||||
// NB: this payload is associated with the clipboard and, once created,
|
||||
// is never destroyed, much like the clipboard itself.
|
||||
DataOfferPayload * payload = malloc(sizeof(struct DataOfferPayload));
|
||||
if (payload) {
|
||||
payload->clipboard = clipboard;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
// Clipboard "devices", one for the actual clipboard and one for the selection clipboard.
|
||||
// Implicitly assumed that WLClipboard can only create once instance of each.
|
||||
static struct wl_data_device *wl_data_device;
|
||||
static struct zwp_primary_selection_device_v1 *zwp_selection_device;
|
||||
|
||||
static void data_device_handle_data_offer(
|
||||
void *data,
|
||||
struct wl_data_device *data_device,
|
||||
struct wl_data_offer *offer);
|
||||
|
||||
static void data_device_handle_selection(
|
||||
void *data,
|
||||
struct wl_data_device *data_device,
|
||||
struct wl_data_offer *offer);
|
||||
|
||||
static void
|
||||
data_device_handle_enter(
|
||||
void *data,
|
||||
struct wl_data_device *wl_data_device,
|
||||
uint32_t serial,
|
||||
struct wl_surface *surface,
|
||||
wl_fixed_t x,
|
||||
wl_fixed_t y,
|
||||
struct wl_data_offer *id)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void
|
||||
data_device_handle_leave(void *data, struct wl_data_device *wl_data_device)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void
|
||||
data_device_handle_motion(
|
||||
void *data,
|
||||
struct wl_data_device *wl_data_device,
|
||||
uint32_t time,
|
||||
wl_fixed_t x,
|
||||
wl_fixed_t y)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void
|
||||
data_device_handle_drop(void *data, struct wl_data_device *wl_data_device)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static const struct wl_data_device_listener wl_data_device_listener = {
|
||||
.data_offer = data_device_handle_data_offer,
|
||||
.selection = data_device_handle_selection,
|
||||
.enter = data_device_handle_enter,
|
||||
.leave = data_device_handle_leave,
|
||||
.motion = data_device_handle_motion
|
||||
};
|
||||
|
||||
static void
|
||||
RegisterDataOfferWithMimeType(DataOfferPayload *payload, void *offer, const char *mime_type)
|
||||
{
|
||||
JNIEnv *env = getEnv();
|
||||
jstring mimeTypeString = (*env)->NewStringUTF(env, mime_type);
|
||||
EXCEPTION_CLEAR(env);
|
||||
if (mimeTypeString) {
|
||||
(*env)->CallVoidMethod(env, payload->clipboard, handleClipboardFormatMID, ptr_to_jlong(offer), mimeTypeString);
|
||||
EXCEPTION_CLEAR(env);
|
||||
(*env)->DeleteLocalRef(env, mimeTypeString);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RegisterDataOffer(DataOfferPayload *payload, void *offer)
|
||||
{
|
||||
JNIEnv *env = getEnv();
|
||||
(*env)->CallVoidMethod(env, payload->clipboard, handleNewClipboardMID, ptr_to_jlong(offer));
|
||||
EXCEPTION_CLEAR(env);
|
||||
}
|
||||
|
||||
static void
|
||||
zwp_selection_offer(
|
||||
void *data,
|
||||
struct zwp_primary_selection_offer_v1 *offer,
|
||||
const char *mime_type)
|
||||
{
|
||||
assert (data != NULL);
|
||||
RegisterDataOfferWithMimeType(data, offer, mime_type);
|
||||
}
|
||||
|
||||
const struct zwp_primary_selection_offer_v1_listener zwp_selection_offer_listener = {
|
||||
.offer = zwp_selection_offer
|
||||
};
|
||||
|
||||
static void
|
||||
zwp_selection_device_handle_data_offer(
|
||||
void *data,
|
||||
struct zwp_primary_selection_device_v1 *device,
|
||||
struct zwp_primary_selection_offer_v1 *offer)
|
||||
{
|
||||
zwp_primary_selection_offer_v1_add_listener(offer, &zwp_selection_offer_listener, data);
|
||||
}
|
||||
|
||||
static void
|
||||
zwp_selection_device_handle_selection(
|
||||
void *data,
|
||||
struct zwp_primary_selection_device_v1 *device,
|
||||
struct zwp_primary_selection_offer_v1 *offer)
|
||||
{
|
||||
assert (data != NULL);
|
||||
RegisterDataOffer(data, offer);
|
||||
}
|
||||
|
||||
static const struct zwp_primary_selection_device_v1_listener zwp_selection_device_listener = {
|
||||
.data_offer = zwp_selection_device_handle_data_offer,
|
||||
.selection = zwp_selection_device_handle_selection
|
||||
};
|
||||
|
||||
static void
|
||||
wl_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action)
|
||||
{
|
||||
// TODO: this is for DnD
|
||||
}
|
||||
|
||||
static void
|
||||
wl_offer(void *data, struct wl_data_offer *offer, const char *mime_type)
|
||||
{
|
||||
assert (data != NULL);
|
||||
RegisterDataOfferWithMimeType(data, offer, mime_type);
|
||||
}
|
||||
|
||||
static void
|
||||
wl_source_actions(void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions)
|
||||
{
|
||||
// TODO: this is for DnD
|
||||
}
|
||||
|
||||
static const struct wl_data_offer_listener wl_data_offer_listener = {
|
||||
.action = wl_action,
|
||||
.offer = wl_offer,
|
||||
.source_actions = wl_source_actions
|
||||
};
|
||||
|
||||
static void
|
||||
data_device_handle_data_offer(
|
||||
void *data,
|
||||
struct wl_data_device *data_device,
|
||||
struct wl_data_offer *offer)
|
||||
{
|
||||
wl_data_offer_add_listener(offer, &wl_data_offer_listener, data);
|
||||
}
|
||||
|
||||
static void data_device_handle_selection(
|
||||
void *data,
|
||||
struct wl_data_device *data_device,
|
||||
struct wl_data_offer *offer)
|
||||
{
|
||||
assert (data != NULL);
|
||||
RegisterDataOffer(data, offer);
|
||||
}
|
||||
|
||||
static void
|
||||
SendClipboardToFD(DataSourcePayload *payload, const char *mime_type, int fd)
|
||||
{
|
||||
JNIEnv *env = getEnv();
|
||||
jstring mime_type_string = (*env)->NewStringUTF(env, mime_type);
|
||||
EXCEPTION_CLEAR(env);
|
||||
if (payload->clipboard != NULL && payload->content != NULL && mime_type_string != NULL && fd >= 0) {
|
||||
(*env)->CallVoidMethod(env,
|
||||
payload->clipboard,
|
||||
transferContentsWithTypeMID,
|
||||
payload->content,
|
||||
mime_type_string,
|
||||
fd);
|
||||
EXCEPTION_CLEAR(env);
|
||||
} else {
|
||||
// The file is normally closed on the Java side, so only close here
|
||||
// if the Java side wasn't involved.
|
||||
close(fd);
|
||||
}
|
||||
if (mime_type_string != NULL) {
|
||||
(*env)->DeleteLocalRef(env, mime_type_string);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CleanupClipboard(DataSourcePayload *payload)
|
||||
{
|
||||
if (payload != NULL) {
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
if (payload->clipboard != NULL) (*env)->DeleteGlobalRef(env, payload->clipboard);
|
||||
if (payload->content != NULL) (*env)->DeleteGlobalRef(env, payload->content);
|
||||
|
||||
DataSourcePayload_Destroy(payload);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_data_source_target(void *data,
|
||||
struct wl_data_source *wl_data_source,
|
||||
const char *mime_type)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void
|
||||
wl_data_source_handle_send(
|
||||
void *data,
|
||||
struct wl_data_source *source,
|
||||
const char *mime_type,
|
||||
int fd)
|
||||
{
|
||||
assert(data);
|
||||
SendClipboardToFD(data, mime_type, fd);
|
||||
}
|
||||
|
||||
static void
|
||||
wl_data_source_handle_cancelled(
|
||||
void *data,
|
||||
struct wl_data_source *source)
|
||||
{
|
||||
CleanupClipboard(data);
|
||||
wl_data_source_destroy(source);
|
||||
}
|
||||
|
||||
static const struct wl_data_source_listener wl_data_source_listener = {
|
||||
.target = wl_data_source_target,
|
||||
.send = wl_data_source_handle_send,
|
||||
.cancelled = wl_data_source_handle_cancelled
|
||||
};
|
||||
|
||||
static void
|
||||
zwp_selection_source_handle_send(
|
||||
void *data,
|
||||
struct zwp_primary_selection_source_v1 *source,
|
||||
const char *mime_type,
|
||||
int32_t fd)
|
||||
{
|
||||
DataSourcePayload * payload = data;
|
||||
assert(payload);
|
||||
|
||||
SendClipboardToFD(payload, mime_type, fd);
|
||||
}
|
||||
|
||||
static void
|
||||
zwp_selection_source_handle_cancelled(
|
||||
void *data,
|
||||
struct zwp_primary_selection_source_v1 *source)
|
||||
{
|
||||
CleanupClipboard(data);
|
||||
zwp_primary_selection_source_v1_destroy(source);
|
||||
}
|
||||
|
||||
static const struct zwp_primary_selection_source_v1_listener zwp_selection_source_listener = {
|
||||
.send = zwp_selection_source_handle_send,
|
||||
.cancelled = zwp_selection_source_handle_cancelled
|
||||
};
|
||||
|
||||
static jboolean
|
||||
initJavaRefs(JNIEnv* env, jclass wlClipboardClass)
|
||||
{
|
||||
GET_METHOD_RETURN(transferContentsWithTypeMID,
|
||||
wlClipboardClass,
|
||||
"transferContentsWithType",
|
||||
"(Ljava/awt/datatransfer/Transferable;Ljava/lang/String;I)V",
|
||||
JNI_FALSE);
|
||||
|
||||
GET_METHOD_RETURN(handleClipboardFormatMID,
|
||||
wlClipboardClass,
|
||||
"handleClipboardFormat",
|
||||
"(JLjava/lang/String;)V",
|
||||
JNI_FALSE);
|
||||
|
||||
GET_METHOD_RETURN(handleNewClipboardMID,
|
||||
wlClipboardClass,
|
||||
"handleNewClipboard",
|
||||
"(J)V",
|
||||
JNI_FALSE);
|
||||
|
||||
GET_FIELD_RETURN(isPrimaryFID,
|
||||
wlClipboardClass,
|
||||
"isPrimary",
|
||||
"Z",
|
||||
JNI_FALSE);
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JNI_TRUE if the WLClipboard referred to by wlClipboard
|
||||
* corresponds to the "primary selection" clipboard and JNI_FALSE
|
||||
* otherwise.
|
||||
* Depending on that, a different protocol must be used to communicate
|
||||
* with Wayland (zwp_primary_selection_device_manager_v1 and
|
||||
* wl_data_device_manager correspondingly).
|
||||
*/
|
||||
static jboolean
|
||||
isPrimarySelectionClipboard(JNIEnv* env, jobject wlClipboard)
|
||||
{
|
||||
return (*env)->GetBooleanField(env, wlClipboard, isPrimaryFID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes data common to all clipboard objects. Called once in
|
||||
* static initialization time of the WLClipboard class.
|
||||
*
|
||||
* Throws InternalError in case of any errors.
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_initIDs(
|
||||
JNIEnv *env,
|
||||
jclass wlClipboardClass)
|
||||
{
|
||||
if (!initJavaRefs(env, wlClipboardClass)) {
|
||||
JNU_ThrowInternalError(env, "Failed to find WLClipboard members");
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_createDataOfferQueue(
|
||||
JNIEnv *env,
|
||||
jclass wlClipboardClass)
|
||||
{
|
||||
struct wl_event_queue * queue = wl_display_create_queue(wl_display);
|
||||
if (queue == NULL) {
|
||||
JNU_ThrowInternalError(env, "Couldn't create an event queue for the clipboard");
|
||||
}
|
||||
|
||||
return ptr_to_jlong(queue);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_dispatchDataOfferQueueImpl(
|
||||
JNIEnv *env,
|
||||
jclass wlClipboardClass,
|
||||
jlong dataOfferQueuePtr)
|
||||
{
|
||||
struct wl_event_queue * queue = jlong_to_ptr(dataOfferQueuePtr);
|
||||
assert (queue != NULL);
|
||||
|
||||
while (wl_display_dispatch_queue(wl_display, queue) != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes data for a specific clipboard object (the primary selection
|
||||
* or the regular one). Called once per clipboard type.
|
||||
*
|
||||
* Returns the native handle to the corresponding clipboard.
|
||||
*
|
||||
* Throws UnsupportedOperationException in case of the primary selection
|
||||
* clipboard is not available and isPrimary is true.
|
||||
* Throws IllegalStateException in case of double-initialization.
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_initNative(
|
||||
JNIEnv *env,
|
||||
jobject obj,
|
||||
jboolean isPrimary)
|
||||
{
|
||||
if (!isPrimary) {
|
||||
if (wl_data_device != NULL) {
|
||||
JNU_ThrowByName(env, "java/lang/IllegalStateException", "one data device has already been created");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (zwp_selection_device != NULL) {
|
||||
JNU_ThrowByName(env,
|
||||
"java/lang/IllegalStateException",
|
||||
"one primary selection device has already been created");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
jobject clipboardGlobalRef = (*env)->NewGlobalRef(env, obj); // normally never deleted
|
||||
CHECK_NULL_RETURN(clipboardGlobalRef, 0);
|
||||
DataOfferPayload *payload = DataOfferPayload_Create(clipboardGlobalRef);
|
||||
if (payload == NULL) {
|
||||
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
|
||||
}
|
||||
CHECK_NULL_THROW_OOME_RETURN(env, payload, "failed to allocate memory for DataOfferPayload", 0);
|
||||
|
||||
if (!isPrimary) {
|
||||
// TODO: may be needed by DnD also, initialize in a common place
|
||||
wl_data_device = wl_data_device_manager_get_data_device(wl_ddm, wl_seat);
|
||||
wl_data_device_add_listener(wl_data_device, &wl_data_device_listener, payload);
|
||||
} else {
|
||||
if (zwp_selection_dm != NULL) {
|
||||
zwp_selection_device = zwp_primary_selection_device_manager_v1_get_device(zwp_selection_dm, wl_seat);
|
||||
zwp_primary_selection_device_v1_add_listener(zwp_selection_device, &zwp_selection_device_listener, payload);
|
||||
} else {
|
||||
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
|
||||
JNU_ThrowByName(env,
|
||||
"java/lang/UnsupportedOperationException",
|
||||
"zwp_primary_selection_device_manager_v1 not available");
|
||||
}
|
||||
}
|
||||
|
||||
return ptr_to_jlong(wl_data_device);
|
||||
}
|
||||
|
||||
static jboolean
|
||||
announceMimeTypesForSource(
|
||||
JNIEnv * env,
|
||||
jboolean isPrimary,
|
||||
jobjectArray mimeTypes,
|
||||
data_source_t source)
|
||||
{
|
||||
jint length = (*env)->GetArrayLength(env, mimeTypes);
|
||||
for (jint i = 0; i < length; i++) {
|
||||
jstring s = (*env)->GetObjectArrayElement(env, mimeTypes, i);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);
|
||||
const char *mimeType = (*env)->GetStringUTFChars(env, s, JNI_FALSE);
|
||||
CHECK_NULL_RETURN(mimeType, JNI_FALSE);
|
||||
|
||||
if (isPrimary) {
|
||||
zwp_primary_selection_source_v1_offer((struct zwp_primary_selection_source_v1 *)source, mimeType);
|
||||
} else {
|
||||
wl_data_source_offer((struct wl_data_source *)source, mimeType);
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, s, mimeType);
|
||||
(*env)->DeleteLocalRef(env, s);
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
static jboolean
|
||||
offerData(
|
||||
JNIEnv* env,
|
||||
DataSourcePayload * payload,
|
||||
jboolean isPrimary,
|
||||
jlong eventSerial,
|
||||
jobjectArray mimeTypes,
|
||||
jlong dataOfferQueuePtr)
|
||||
{
|
||||
data_source_t source = isPrimary
|
||||
? (data_source_t)zwp_primary_selection_device_manager_v1_create_source(zwp_selection_dm)
|
||||
: (data_source_t)wl_data_device_manager_create_data_source(wl_ddm);
|
||||
|
||||
if (source != NULL) {
|
||||
wl_proxy_set_queue((struct wl_proxy*)source, jlong_to_ptr(dataOfferQueuePtr));
|
||||
|
||||
if (isPrimary) {
|
||||
zwp_primary_selection_source_v1_add_listener(
|
||||
(struct zwp_primary_selection_source_v1 *)source,
|
||||
&zwp_selection_source_listener,
|
||||
payload);
|
||||
} else {
|
||||
wl_data_source_add_listener(
|
||||
(struct wl_data_source *)source,
|
||||
&wl_data_source_listener,
|
||||
payload);
|
||||
}
|
||||
|
||||
if (mimeTypes != NULL) {
|
||||
if (!announceMimeTypesForSource(env, isPrimary, mimeTypes, source)) {
|
||||
if (isPrimary) {
|
||||
zwp_primary_selection_source_v1_destroy(source);
|
||||
} else {
|
||||
wl_data_source_destroy(source);
|
||||
}
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPrimary) {
|
||||
zwp_primary_selection_device_v1_set_selection(
|
||||
zwp_selection_device,
|
||||
(struct zwp_primary_selection_source_v1 *)source,
|
||||
eventSerial);
|
||||
}
|
||||
else {
|
||||
wl_data_device_set_selection(
|
||||
wl_data_device,
|
||||
(struct wl_data_source *)source,
|
||||
eventSerial);
|
||||
}
|
||||
}
|
||||
|
||||
return source != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes Wayland aware of the availability of new clipboard content in the
|
||||
* given MIME formats.
|
||||
* Retains the reference to clipboard content for the further use when the actual
|
||||
* clipboard data get requested.
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_offerData(
|
||||
JNIEnv *env,
|
||||
jobject obj,
|
||||
jlong eventSerial,
|
||||
jobjectArray mimeTypes,
|
||||
jobject content,
|
||||
jlong dataOfferQueuePtr)
|
||||
{
|
||||
jobject clipboardGlobalRef = (*env)->NewGlobalRef(env, obj); // deleted by ...source_handle_cancelled()
|
||||
CHECK_NULL(clipboardGlobalRef);
|
||||
jobject contentGlobalRef = (*env)->NewGlobalRef(env, content); // deleted by ...source_handle_cancelled()
|
||||
CHECK_NULL(contentGlobalRef);
|
||||
|
||||
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");
|
||||
|
||||
const jboolean isPrimary = isPrimarySelectionClipboard(env, obj);
|
||||
if (!offerData(env,payload, isPrimary, eventSerial, mimeTypes, dataOfferQueuePtr)) {
|
||||
// Failed to create a data source; give up and cleanup.
|
||||
CleanupClipboard(payload);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_cancelOffer(
|
||||
JNIEnv *env,
|
||||
jobject obj,
|
||||
jlong eventSerial)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* in case of an error.
|
||||
*
|
||||
* NB: the returned file descriptor must be closed by the caller.
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_requestDataInFormat(
|
||||
JNIEnv *env,
|
||||
jobject obj,
|
||||
jlong clipboardNativePtr,
|
||||
jstring mimeTypeJava)
|
||||
{
|
||||
assert (clipboardNativePtr != 0);
|
||||
const jboolean isPrimary = isPrimarySelectionClipboard(env, obj);
|
||||
|
||||
int fd = -1; // The file descriptor the clipboard data will be read from by Java
|
||||
|
||||
const char * mimeType = (*env)->GetStringUTFChars(env, mimeTypeJava, NULL);
|
||||
if (mimeType) {
|
||||
int fds[2];
|
||||
int rc = pipe(fds);
|
||||
if (rc == 0) {
|
||||
if (isPrimary) {
|
||||
struct zwp_primary_selection_offer_v1 * offer = jlong_to_ptr(clipboardNativePtr);
|
||||
zwp_primary_selection_offer_v1_receive(offer, mimeType, fds[1]);
|
||||
} else {
|
||||
struct wl_data_offer * offer = jlong_to_ptr(clipboardNativePtr);
|
||||
wl_data_offer_receive(offer, mimeType, fds[1]);
|
||||
}
|
||||
close(fds[1]); // close the "sender" end of the pipe
|
||||
fd = fds[0];
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, mimeTypeJava, mimeType);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the corresponding Wayland proxy objects pointed to by clipboardNativePtr.
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLClipboard_destroyClipboard(
|
||||
JNIEnv *env,
|
||||
jobject obj,
|
||||
jlong clipboardNativePtr)
|
||||
{
|
||||
assert(clipboardNativePtr != 0);
|
||||
|
||||
if (isPrimarySelectionClipboard(env, obj)) {
|
||||
struct zwp_primary_selection_offer_v1 * offer = jlong_to_ptr(clipboardNativePtr);
|
||||
zwp_primary_selection_offer_v1_destroy(offer);
|
||||
} else {
|
||||
struct wl_data_offer * offer = jlong_to_ptr(clipboardNativePtr);
|
||||
wl_data_offer_destroy(offer);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <assert.h>
|
||||
#include <gtk-shell1-client-protocol.h>
|
||||
|
||||
#include "jni_util.h"
|
||||
#include "JNIUtilities.h"
|
||||
#include "WLToolkit.h"
|
||||
#include "WLRobotPeer.h"
|
||||
#include "WLGraphicsEnvironment.h"
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <Trace.h>
|
||||
|
||||
#include "jni_util.h"
|
||||
#include "JNIUtilities.h"
|
||||
#include "WLToolkit.h"
|
||||
#include "VKBase.h"
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "gtk-shell1-client-protocol.h"
|
||||
|
||||
#include "jvm_md.h"
|
||||
#include "jni_util.h"
|
||||
#include "JNIUtilities.h"
|
||||
#include "awt.h"
|
||||
#include "sun_awt_wl_WLToolkit.h"
|
||||
#include "WLToolkit.h"
|
||||
@@ -70,6 +70,8 @@ struct wl_pointer *wl_pointer;
|
||||
struct wl_cursor_theme *wl_cursor_theme = NULL;
|
||||
|
||||
struct wl_surface *wl_surface_in_focus = NULL;
|
||||
struct wl_data_device_manager *wl_ddm = NULL;
|
||||
struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm = NULL;
|
||||
|
||||
uint32_t last_mouse_pressed_serial = 0;
|
||||
uint32_t last_pointer_enter_serial = 0;
|
||||
@@ -689,7 +691,12 @@ registry_global(void *data, struct wl_registry *wl_registry,
|
||||
xdg_activation_v1 = wl_registry_bind(wl_registry, name, &xdg_activation_v1_interface, 1);
|
||||
} else if (strcmp(interface, gtk_shell1_interface.name) == 0) {
|
||||
gtk_shell1 = wl_registry_bind(wl_registry, name, >k_shell1_interface, 1);
|
||||
} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
|
||||
wl_ddm = wl_registry_bind(wl_registry, name,&wl_data_device_manager_interface, 3);
|
||||
} else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
|
||||
zwp_selection_dm = wl_registry_bind(wl_registry, name,&zwp_primary_selection_device_manager_v1_interface, 1);
|
||||
}
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
else if (strcmp(interface, wakefield_interface.name) == 0) {
|
||||
wakefield = wl_registry_bind(wl_registry, name, &wakefield_interface, 1);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <wayland-cursor.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "xdg-activation-v1-client-protocol.h"
|
||||
#include "primary-selection-client-protocol.h"
|
||||
|
||||
#define CHECK_NULL_THROW_OOME_RETURN(env, x, msg, z)\
|
||||
do { \
|
||||
@@ -55,6 +56,8 @@ extern struct xdg_activation_v1 *xdg_activation_v1;
|
||||
extern struct gtk_shell1* gtk_shell1; // optional, check for NULL before use
|
||||
|
||||
extern struct wl_cursor_theme *wl_cursor_theme;
|
||||
extern struct wl_data_device_manager *wl_ddm;
|
||||
extern struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm;
|
||||
|
||||
extern struct wl_surface *wl_surface_in_focus;
|
||||
|
||||
|
||||
@@ -0,0 +1,590 @@
|
||||
/* Generated by wayland-scanner 1.19.0 */
|
||||
|
||||
#ifndef WP_PRIMARY_SELECTION_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define WP_PRIMARY_SELECTION_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_wp_primary_selection_unstable_v1 The wp_primary_selection_unstable_v1 protocol
|
||||
* Primary selection protocol
|
||||
*
|
||||
* @section page_desc_wp_primary_selection_unstable_v1 Description
|
||||
*
|
||||
* This protocol provides the ability to have a primary selection device to
|
||||
* match that of the X server. This primary selection is a shortcut to the
|
||||
* common clipboard selection, where text just needs to be selected in order
|
||||
* to allow copying it elsewhere. The de facto way to perform this action
|
||||
* is the middle mouse button, although it is not limited to this one.
|
||||
*
|
||||
* Clients wishing to honor primary selection should create a primary
|
||||
* selection source and set it as the selection through
|
||||
* wp_primary_selection_device.set_selection whenever the text selection
|
||||
* changes. In order to minimize calls in pointer-driven text selection,
|
||||
* it should happen only once after the operation finished. Similarly,
|
||||
* a NULL source should be set when text is unselected.
|
||||
*
|
||||
* wp_primary_selection_offer objects are first announced through the
|
||||
* wp_primary_selection_device.data_offer event. Immediately after this event,
|
||||
* the primary data offer will emit wp_primary_selection_offer.offer events
|
||||
* to let know of the mime types being offered.
|
||||
*
|
||||
* When the primary selection changes, the client with the keyboard focus
|
||||
* will receive wp_primary_selection_device.selection events. Only the client
|
||||
* with the keyboard focus will receive such events with a non-NULL
|
||||
* wp_primary_selection_offer. Across keyboard focus changes, previously
|
||||
* focused clients will receive wp_primary_selection_device.events with a
|
||||
* NULL wp_primary_selection_offer.
|
||||
*
|
||||
* In order to request the primary selection data, the client must pass
|
||||
* a recent serial pertaining to the press event that is triggering the
|
||||
* operation, if the compositor deems the serial valid and recent, the
|
||||
* wp_primary_selection_source.send event will happen in the other end
|
||||
* to let the transfer begin. The client owning the primary selection
|
||||
* should write the requested data, and close the file descriptor
|
||||
* immediately.
|
||||
*
|
||||
* If the primary selection owner client disappeared during the transfer,
|
||||
* the client reading the data will receive a
|
||||
* wp_primary_selection_device.selection event with a NULL
|
||||
* wp_primary_selection_offer, the client should take this as a hint
|
||||
* to finish the reads related to the no longer existing offer.
|
||||
*
|
||||
* The primary selection owner should be checking for errors during
|
||||
* writes, merely cancelling the ongoing transfer if any happened.
|
||||
*
|
||||
* @section page_ifaces_wp_primary_selection_unstable_v1 Interfaces
|
||||
* - @subpage page_iface_zwp_primary_selection_device_manager_v1 - X primary selection emulation
|
||||
* - @subpage page_iface_zwp_primary_selection_device_v1 -
|
||||
* - @subpage page_iface_zwp_primary_selection_offer_v1 - offer to transfer primary selection contents
|
||||
* - @subpage page_iface_zwp_primary_selection_source_v1 - offer to replace the contents of the primary selection
|
||||
* @section page_copyright_wp_primary_selection_unstable_v1 Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright © 2015, 2016 Red Hat
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
* </pre>
|
||||
*/
|
||||
struct wl_seat;
|
||||
struct zwp_primary_selection_device_manager_v1;
|
||||
struct zwp_primary_selection_device_v1;
|
||||
struct zwp_primary_selection_offer_v1;
|
||||
struct zwp_primary_selection_source_v1;
|
||||
|
||||
#ifndef ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_INTERFACE
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwp_primary_selection_device_manager_v1 zwp_primary_selection_device_manager_v1
|
||||
* @section page_iface_zwp_primary_selection_device_manager_v1_desc Description
|
||||
*
|
||||
* The primary selection device manager is a singleton global object that
|
||||
* provides access to the primary selection. It allows to create
|
||||
* wp_primary_selection_source objects, as well as retrieving the per-seat
|
||||
* wp_primary_selection_device objects.
|
||||
* @section page_iface_zwp_primary_selection_device_manager_v1_api API
|
||||
* See @ref iface_zwp_primary_selection_device_manager_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwp_primary_selection_device_manager_v1 The zwp_primary_selection_device_manager_v1 interface
|
||||
*
|
||||
* The primary selection device manager is a singleton global object that
|
||||
* provides access to the primary selection. It allows to create
|
||||
* wp_primary_selection_source objects, as well as retrieving the per-seat
|
||||
* wp_primary_selection_device objects.
|
||||
*/
|
||||
extern const struct wl_interface zwp_primary_selection_device_manager_v1_interface;
|
||||
#endif
|
||||
#ifndef ZWP_PRIMARY_SELECTION_DEVICE_V1_INTERFACE
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwp_primary_selection_device_v1 zwp_primary_selection_device_v1
|
||||
* @section page_iface_zwp_primary_selection_device_v1_api API
|
||||
* See @ref iface_zwp_primary_selection_device_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwp_primary_selection_device_v1 The zwp_primary_selection_device_v1 interface
|
||||
*/
|
||||
extern const struct wl_interface zwp_primary_selection_device_v1_interface;
|
||||
#endif
|
||||
#ifndef ZWP_PRIMARY_SELECTION_OFFER_V1_INTERFACE
|
||||
#define ZWP_PRIMARY_SELECTION_OFFER_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwp_primary_selection_offer_v1 zwp_primary_selection_offer_v1
|
||||
* @section page_iface_zwp_primary_selection_offer_v1_desc Description
|
||||
*
|
||||
* A wp_primary_selection_offer represents an offer to transfer the contents
|
||||
* of the primary selection clipboard to the client. Similar to
|
||||
* wl_data_offer, the offer also describes the mime types that the data can
|
||||
* be converted to and provides the mechanisms for transferring the data
|
||||
* directly to the client.
|
||||
* @section page_iface_zwp_primary_selection_offer_v1_api API
|
||||
* See @ref iface_zwp_primary_selection_offer_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwp_primary_selection_offer_v1 The zwp_primary_selection_offer_v1 interface
|
||||
*
|
||||
* A wp_primary_selection_offer represents an offer to transfer the contents
|
||||
* of the primary selection clipboard to the client. Similar to
|
||||
* wl_data_offer, the offer also describes the mime types that the data can
|
||||
* be converted to and provides the mechanisms for transferring the data
|
||||
* directly to the client.
|
||||
*/
|
||||
extern const struct wl_interface zwp_primary_selection_offer_v1_interface;
|
||||
#endif
|
||||
#ifndef ZWP_PRIMARY_SELECTION_SOURCE_V1_INTERFACE
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwp_primary_selection_source_v1 zwp_primary_selection_source_v1
|
||||
* @section page_iface_zwp_primary_selection_source_v1_desc Description
|
||||
*
|
||||
* The source side of a wp_primary_selection_offer, it provides a way to
|
||||
* describe the offered data and respond to requests to transfer the
|
||||
* requested contents of the primary selection clipboard.
|
||||
* @section page_iface_zwp_primary_selection_source_v1_api API
|
||||
* See @ref iface_zwp_primary_selection_source_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwp_primary_selection_source_v1 The zwp_primary_selection_source_v1 interface
|
||||
*
|
||||
* The source side of a wp_primary_selection_offer, it provides a way to
|
||||
* describe the offered data and respond to requests to transfer the
|
||||
* requested contents of the primary selection clipboard.
|
||||
*/
|
||||
extern const struct wl_interface zwp_primary_selection_source_v1_interface;
|
||||
#endif
|
||||
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_CREATE_SOURCE 0
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_GET_DEVICE 1
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_DESTROY 2
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_manager_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_CREATE_SOURCE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_manager_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_GET_DEVICE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_manager_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_device_manager_v1 */
|
||||
static inline void
|
||||
zwp_primary_selection_device_manager_v1_set_user_data(struct zwp_primary_selection_device_manager_v1 *zwp_primary_selection_device_manager_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwp_primary_selection_device_manager_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_device_manager_v1 */
|
||||
static inline void *
|
||||
zwp_primary_selection_device_manager_v1_get_user_data(struct zwp_primary_selection_device_manager_v1 *zwp_primary_selection_device_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwp_primary_selection_device_manager_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwp_primary_selection_device_manager_v1_get_version(struct zwp_primary_selection_device_manager_v1 *zwp_primary_selection_device_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwp_primary_selection_device_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_manager_v1
|
||||
*
|
||||
* Create a new primary selection source.
|
||||
*/
|
||||
static inline struct zwp_primary_selection_source_v1 *
|
||||
zwp_primary_selection_device_manager_v1_create_source(struct zwp_primary_selection_device_manager_v1 *zwp_primary_selection_device_manager_v1)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_primary_selection_device_manager_v1,
|
||||
ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_CREATE_SOURCE, &zwp_primary_selection_source_v1_interface, NULL);
|
||||
|
||||
return (struct zwp_primary_selection_source_v1 *) id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_manager_v1
|
||||
*
|
||||
* Create a new data device for a given seat.
|
||||
*/
|
||||
static inline struct zwp_primary_selection_device_v1 *
|
||||
zwp_primary_selection_device_manager_v1_get_device(struct zwp_primary_selection_device_manager_v1 *zwp_primary_selection_device_manager_v1, struct wl_seat *seat)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_primary_selection_device_manager_v1,
|
||||
ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_GET_DEVICE, &zwp_primary_selection_device_v1_interface, NULL, seat);
|
||||
|
||||
return (struct zwp_primary_selection_device_v1 *) id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_manager_v1
|
||||
*
|
||||
* Destroy the primary selection device manager.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_device_manager_v1_destroy(struct zwp_primary_selection_device_manager_v1 *zwp_primary_selection_device_manager_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_device_manager_v1,
|
||||
ZWP_PRIMARY_SELECTION_DEVICE_MANAGER_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwp_primary_selection_device_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
* @struct zwp_primary_selection_device_v1_listener
|
||||
*/
|
||||
struct zwp_primary_selection_device_v1_listener {
|
||||
/**
|
||||
* introduce a new wp_primary_selection_offer
|
||||
*
|
||||
* Introduces a new wp_primary_selection_offer object that may be
|
||||
* used to receive the current primary selection. Immediately
|
||||
* following this event, the new wp_primary_selection_offer object
|
||||
* will send wp_primary_selection_offer.offer events to describe
|
||||
* the offered mime types.
|
||||
*/
|
||||
void (*data_offer)(void *data,
|
||||
struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
|
||||
struct zwp_primary_selection_offer_v1 *offer);
|
||||
/**
|
||||
* advertise a new primary selection
|
||||
*
|
||||
* The wp_primary_selection_device.selection event is sent to
|
||||
* notify the client of a new primary selection. This event is sent
|
||||
* after the wp_primary_selection.data_offer event introducing this
|
||||
* object, and after the offer has announced its mimetypes through
|
||||
* wp_primary_selection_offer.offer.
|
||||
*
|
||||
* The data_offer is valid until a new offer or NULL is received or
|
||||
* until the client loses keyboard focus. The client must destroy
|
||||
* the previous selection data_offer, if any, upon receiving this
|
||||
* event.
|
||||
*/
|
||||
void (*selection)(void *data,
|
||||
struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
|
||||
struct zwp_primary_selection_offer_v1 *id);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*/
|
||||
static inline int
|
||||
zwp_primary_selection_device_v1_add_listener(struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
|
||||
const struct zwp_primary_selection_device_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwp_primary_selection_device_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_SET_SELECTION 0
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_DESTROY 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_DATA_OFFER_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_SELECTION_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_SET_SELECTION_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_DEVICE_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_device_v1 */
|
||||
static inline void
|
||||
zwp_primary_selection_device_v1_set_user_data(struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwp_primary_selection_device_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_device_v1 */
|
||||
static inline void *
|
||||
zwp_primary_selection_device_v1_get_user_data(struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwp_primary_selection_device_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwp_primary_selection_device_v1_get_version(struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwp_primary_selection_device_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*
|
||||
* Replaces the current selection. The previous owner of the primary
|
||||
* selection will receive a wp_primary_selection_source.cancelled event.
|
||||
*
|
||||
* To unset the selection, set the source to NULL.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_device_v1_set_selection(struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1, struct zwp_primary_selection_source_v1 *source, uint32_t serial)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_device_v1,
|
||||
ZWP_PRIMARY_SELECTION_DEVICE_V1_SET_SELECTION, source, serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_device_v1
|
||||
*
|
||||
* Destroy the primary selection device.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_device_v1_destroy(struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_device_v1,
|
||||
ZWP_PRIMARY_SELECTION_DEVICE_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwp_primary_selection_device_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
* @struct zwp_primary_selection_offer_v1_listener
|
||||
*/
|
||||
struct zwp_primary_selection_offer_v1_listener {
|
||||
/**
|
||||
* advertise offered mime type
|
||||
*
|
||||
* Sent immediately after creating announcing the
|
||||
* wp_primary_selection_offer through
|
||||
* wp_primary_selection_device.data_offer. One event is sent per
|
||||
* offered mime type.
|
||||
*/
|
||||
void (*offer)(void *data,
|
||||
struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1,
|
||||
const char *mime_type);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
*/
|
||||
static inline int
|
||||
zwp_primary_selection_offer_v1_add_listener(struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1,
|
||||
const struct zwp_primary_selection_offer_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwp_primary_selection_offer_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWP_PRIMARY_SELECTION_OFFER_V1_RECEIVE 0
|
||||
#define ZWP_PRIMARY_SELECTION_OFFER_V1_DESTROY 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_OFFER_V1_OFFER_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_OFFER_V1_RECEIVE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_OFFER_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_offer_v1 */
|
||||
static inline void
|
||||
zwp_primary_selection_offer_v1_set_user_data(struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwp_primary_selection_offer_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_offer_v1 */
|
||||
static inline void *
|
||||
zwp_primary_selection_offer_v1_get_user_data(struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwp_primary_selection_offer_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwp_primary_selection_offer_v1_get_version(struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwp_primary_selection_offer_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
*
|
||||
* To transfer the contents of the primary selection clipboard, the client
|
||||
* issues this request and indicates the mime type that it wants to
|
||||
* receive. The transfer happens through the passed file descriptor
|
||||
* (typically created with the pipe system call). The source client writes
|
||||
* the data in the mime type representation requested and then closes the
|
||||
* file descriptor.
|
||||
*
|
||||
* The receiving client reads from the read end of the pipe until EOF and
|
||||
* closes its end, at which point the transfer is complete.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_offer_v1_receive(struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type, int32_t fd)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_offer_v1,
|
||||
ZWP_PRIMARY_SELECTION_OFFER_V1_RECEIVE, mime_type, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_offer_v1
|
||||
*
|
||||
* Destroy the primary selection offer.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_offer_v1_destroy(struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_offer_v1,
|
||||
ZWP_PRIMARY_SELECTION_OFFER_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwp_primary_selection_offer_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
* @struct zwp_primary_selection_source_v1_listener
|
||||
*/
|
||||
struct zwp_primary_selection_source_v1_listener {
|
||||
/**
|
||||
* send the primary selection contents
|
||||
*
|
||||
* Request for the current primary selection contents from the
|
||||
* client. Send the specified mime type over the passed file
|
||||
* descriptor, then close it.
|
||||
*/
|
||||
void (*send)(void *data,
|
||||
struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
|
||||
const char *mime_type,
|
||||
int32_t fd);
|
||||
/**
|
||||
* request for primary selection contents was canceled
|
||||
*
|
||||
* This primary selection source is no longer valid. The client
|
||||
* should clean up and destroy this primary selection source.
|
||||
*/
|
||||
void (*cancelled)(void *data,
|
||||
struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*/
|
||||
static inline int
|
||||
zwp_primary_selection_source_v1_add_listener(struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
|
||||
const struct zwp_primary_selection_source_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwp_primary_selection_source_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_OFFER 0
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_DESTROY 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_SEND_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_CANCELLED_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_OFFER_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*/
|
||||
#define ZWP_PRIMARY_SELECTION_SOURCE_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_source_v1 */
|
||||
static inline void
|
||||
zwp_primary_selection_source_v1_set_user_data(struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwp_primary_selection_source_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwp_primary_selection_source_v1 */
|
||||
static inline void *
|
||||
zwp_primary_selection_source_v1_get_user_data(struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwp_primary_selection_source_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwp_primary_selection_source_v1_get_version(struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwp_primary_selection_source_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*
|
||||
* This request adds a mime type to the set of mime types advertised to
|
||||
* targets. Can be called several times to offer multiple types.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_source_v1_offer(struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1, const char *mime_type)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_source_v1,
|
||||
ZWP_PRIMARY_SELECTION_SOURCE_V1_OFFER, mime_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwp_primary_selection_source_v1
|
||||
*
|
||||
* Destroy the primary selection source.
|
||||
*/
|
||||
static inline void
|
||||
zwp_primary_selection_source_v1_destroy(struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwp_primary_selection_source_v1,
|
||||
ZWP_PRIMARY_SELECTION_SOURCE_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwp_primary_selection_source_v1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
/* Generated by wayland-scanner 1.19.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2015, 2016 Red Hat
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface zwp_primary_selection_device_v1_interface;
|
||||
extern const struct wl_interface zwp_primary_selection_offer_v1_interface;
|
||||
extern const struct wl_interface zwp_primary_selection_source_v1_interface;
|
||||
|
||||
static const struct wl_interface *wp_primary_selection_unstable_v1_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
&zwp_primary_selection_source_v1_interface,
|
||||
&zwp_primary_selection_device_v1_interface,
|
||||
&wl_seat_interface,
|
||||
&zwp_primary_selection_source_v1_interface,
|
||||
NULL,
|
||||
&zwp_primary_selection_offer_v1_interface,
|
||||
&zwp_primary_selection_offer_v1_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_device_manager_v1_requests[] = {
|
||||
{ "create_source", "n", wp_primary_selection_unstable_v1_types + 2 },
|
||||
{ "get_device", "no", wp_primary_selection_unstable_v1_types + 3 },
|
||||
{ "destroy", "", wp_primary_selection_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwp_primary_selection_device_manager_v1_interface = {
|
||||
"zwp_primary_selection_device_manager_v1", 1,
|
||||
3, zwp_primary_selection_device_manager_v1_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_device_v1_requests[] = {
|
||||
{ "set_selection", "?ou", wp_primary_selection_unstable_v1_types + 5 },
|
||||
{ "destroy", "", wp_primary_selection_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_device_v1_events[] = {
|
||||
{ "data_offer", "n", wp_primary_selection_unstable_v1_types + 7 },
|
||||
{ "selection", "?o", wp_primary_selection_unstable_v1_types + 8 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwp_primary_selection_device_v1_interface = {
|
||||
"zwp_primary_selection_device_v1", 1,
|
||||
2, zwp_primary_selection_device_v1_requests,
|
||||
2, zwp_primary_selection_device_v1_events,
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_offer_v1_requests[] = {
|
||||
{ "receive", "sh", wp_primary_selection_unstable_v1_types + 0 },
|
||||
{ "destroy", "", wp_primary_selection_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_offer_v1_events[] = {
|
||||
{ "offer", "s", wp_primary_selection_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwp_primary_selection_offer_v1_interface = {
|
||||
"zwp_primary_selection_offer_v1", 1,
|
||||
2, zwp_primary_selection_offer_v1_requests,
|
||||
1, zwp_primary_selection_offer_v1_events,
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_source_v1_requests[] = {
|
||||
{ "offer", "s", wp_primary_selection_unstable_v1_types + 0 },
|
||||
{ "destroy", "", wp_primary_selection_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwp_primary_selection_source_v1_events[] = {
|
||||
{ "send", "sh", wp_primary_selection_unstable_v1_types + 0 },
|
||||
{ "cancelled", "", wp_primary_selection_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwp_primary_selection_source_v1_interface = {
|
||||
"zwp_primary_selection_source_v1", 1,
|
||||
2, zwp_primary_selection_source_v1_requests,
|
||||
2, zwp_primary_selection_source_v1_events,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user