JBR-5857 Wayland: implement clipboard support

This commit is contained in:
Maxim Kartashev
2023-07-20 19:54:57 +04:00
parent 5e738482b1
commit 6a1c5bd43a
12 changed files with 2399 additions and 10 deletions

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

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

View File

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

View File

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

View 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_

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

View File

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

View File

@@ -29,7 +29,7 @@
#include <stdlib.h>
#include <Trace.h>
#include "jni_util.h"
#include "JNIUtilities.h"
#include "WLToolkit.h"
#include "VKBase.h"

View File

@@ -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, &gtk_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);

View File

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

View File

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

View File

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