Path: blob/master/src/java.desktop/unix/classes/sun/awt/X11/XClipboard.java
41159 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt.X11;2627import java.awt.datatransfer.Transferable;28import java.awt.datatransfer.DataFlavor;29import java.util.SortedMap;30import java.io.IOException;31import java.security.AccessController;32import java.util.HashMap;33import java.util.Map;34import sun.awt.UNIXToolkit;35import sun.awt.datatransfer.DataTransferer;36import sun.awt.datatransfer.SunClipboard;37import sun.awt.datatransfer.ClipboardTransferable;38import sun.security.action.GetIntegerAction;3940/**41* A class which interfaces with the X11 selection service in order to support42* data transfer via Clipboard operations.43*/44public final class XClipboard extends SunClipboard implements OwnershipListener45{46private final XSelection selection;47// Time of calling XConvertSelection().48private long convertSelectionTime;49// The flag used not to call XConvertSelection() if the previous SelectionNotify50// has not been processed by checkChange().51private volatile boolean isSelectionNotifyProcessed;52// The property in which the owner should place requested targets53// when tracking changes of available data flavors (practically targets).54private volatile XAtom targetsPropertyAtom;5556private static final Object classLock = new Object();5758private static final int defaultPollInterval = 200;5960private static int pollInterval;6162private static Map<Long, XClipboard> targetsAtom2Clipboard;6364/**65* Creates a system clipboard object.66*/67public XClipboard(String name, String selectionName) {68super(name);69selection = new XSelection(XAtom.get(selectionName));70selection.registerOwershipListener(this);71}7273/*74* NOTE: This method may be called by privileged threads.75* DO NOT INVOKE CLIENT CODE ON THIS THREAD!76*/77public void ownershipChanged(final boolean isOwner) {78if (isOwner) {79checkChangeHere(contents);80} else {81lostOwnershipImpl();82}83}8485protected synchronized void setContentsNative(Transferable contents) {86SortedMap<Long,DataFlavor> formatMap =87DataTransferer.getInstance().getFormatsForTransferable88(contents, DataTransferer.adaptFlavorMap(getDefaultFlavorTable()));89long[] formats = DataTransferer.keysToLongArray(formatMap);9091if (!selection.setOwner(contents, formatMap, formats,92XToolkit.getCurrentServerTime())) {93this.owner = null;94this.contents = null;95}96}9798public long getID() {99return selection.getSelectionAtom().getAtom();100}101102@Override103public synchronized Transferable getContents(Object requestor) {104if (contents != null) {105return contents;106}107return new ClipboardTransferable(this);108}109110/* Caller is synchronized on this. */111protected void clearNativeContext() {112selection.reset();113}114115116protected long[] getClipboardFormats() {117return selection.getTargets(XToolkit.getCurrentServerTime());118}119120protected byte[] getClipboardData(long format) throws IOException {121return selection.getData(format, XToolkit.getCurrentServerTime());122}123124private void checkChangeHere(Transferable contents) {125if (areFlavorListenersRegistered()) {126checkChange(DataTransferer.getInstance().127getFormatsForTransferableAsArray(contents, getDefaultFlavorTable()));128}129}130131@SuppressWarnings("removal")132private static int getPollInterval() {133synchronized (XClipboard.classLock) {134if (pollInterval <= 0) {135pollInterval = AccessController.doPrivileged(136new GetIntegerAction("awt.datatransfer.clipboard.poll.interval",137defaultPollInterval));138if (pollInterval <= 0) {139pollInterval = defaultPollInterval;140}141}142return pollInterval;143}144}145146private XAtom getTargetsPropertyAtom() {147if (null == targetsPropertyAtom) {148targetsPropertyAtom =149XAtom.get("XAWT_TARGETS_OF_SELECTION:" + selection.getSelectionAtom().getName());150}151return targetsPropertyAtom;152}153154protected void registerClipboardViewerChecked() {155// for XConvertSelection() to be called for the first time in getTargetsDelayed()156isSelectionNotifyProcessed = true;157158boolean mustSchedule = false;159XToolkit.awtLock();160try {161synchronized (XClipboard.classLock) {162if (targetsAtom2Clipboard == null) {163targetsAtom2Clipboard = new HashMap<Long, XClipboard>(2);164}165mustSchedule = targetsAtom2Clipboard.isEmpty();166targetsAtom2Clipboard.put(getTargetsPropertyAtom().getAtom(), this);167if (mustSchedule) {168XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),169new SelectionNotifyHandler());170}171}172if (mustSchedule) {173XToolkit.schedule(new CheckChangeTimerTask(), XClipboard.getPollInterval());174}175} finally {176XToolkit.awtUnlock();177}178}179180private static class CheckChangeTimerTask implements Runnable {181public void run() {182for (XClipboard clpbrd : targetsAtom2Clipboard.values()) {183clpbrd.getTargetsDelayed();184}185synchronized (XClipboard.classLock) {186if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) {187// The viewer is still registered, schedule next poll.188XToolkit.schedule(this, XClipboard.getPollInterval());189}190}191}192}193194private static class SelectionNotifyHandler implements XEventDispatcher {195public void dispatchEvent(XEvent ev) {196if (ev.get_type() == XConstants.SelectionNotify) {197final XSelectionEvent xse = ev.get_xselection();198XClipboard clipboard = null;199synchronized (XClipboard.classLock) {200if (targetsAtom2Clipboard != null && targetsAtom2Clipboard.isEmpty()) {201// The viewer was unregistered, remove the dispatcher.202XToolkit.removeEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), this);203return;204}205final long propertyAtom = xse.get_property();206clipboard = targetsAtom2Clipboard.get(propertyAtom);207}208if (null != clipboard) {209clipboard.checkChange(xse);210}211}212}213}214215protected void unregisterClipboardViewerChecked() {216isSelectionNotifyProcessed = false;217synchronized (XClipboard.classLock) {218targetsAtom2Clipboard.remove(getTargetsPropertyAtom().getAtom());219}220}221222// checkChange() will be called on SelectionNotify223private void getTargetsDelayed() {224XToolkit.awtLock();225try {226long curTime = System.currentTimeMillis();227if (isSelectionNotifyProcessed || curTime >= (convertSelectionTime + UNIXToolkit.getDatatransferTimeout()))228{229convertSelectionTime = curTime;230XlibWrapper.XConvertSelection(XToolkit.getDisplay(),231selection.getSelectionAtom().getAtom(),232XDataTransferer.TARGETS_ATOM.getAtom(),233getTargetsPropertyAtom().getAtom(),234XWindow.getXAWTRootWindow().getWindow(),235XConstants.CurrentTime);236isSelectionNotifyProcessed = false;237}238} finally {239XToolkit.awtUnlock();240}241}242243/*244* Tracks changes of available formats.245* NOTE: This method may be called by privileged threads.246* DO NOT INVOKE CLIENT CODE ON THIS THREAD!247*/248private void checkChange(XSelectionEvent xse) {249final long propertyAtom = xse.get_property();250if (propertyAtom != getTargetsPropertyAtom().getAtom()) {251// wrong atom252return;253}254255final XAtom selectionAtom = XAtom.get(xse.get_selection());256final XSelection changedSelection = XSelection.getSelection(selectionAtom);257258if (null == changedSelection || changedSelection != selection) {259// unknown selection - do nothing260return;261}262263isSelectionNotifyProcessed = true;264265if (selection.isOwner()) {266// selection is owner - do not need formats267return;268}269270long[] formats = null;271272if (propertyAtom == XConstants.None) {273// We treat None property atom as "empty selection".274formats = new long[0];275} else {276WindowPropertyGetter targetsGetter =277new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),278XAtom.get(propertyAtom), 0,279XSelection.MAX_LENGTH, true,280XConstants.AnyPropertyType);281try {282targetsGetter.execute();283formats = XSelection.getFormats(targetsGetter);284} finally {285targetsGetter.dispose();286}287}288289XToolkit.awtUnlock();290try {291checkChange(formats);292} finally {293XToolkit.awtLock();294}295}296}297298299