jdk/src/solaris/classes/sun/awt/X11/XClipboard.java
changeset 117 766ae458aaf1
parent 2 90ce3da70b43
child 439 3488710b02f8
--- a/jdk/src/solaris/classes/sun/awt/X11/XClipboard.java	Thu Mar 13 17:08:15 2008 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XClipboard.java	Thu Mar 13 17:14:44 2008 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2008 Sun Microsystems, Inc.  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
@@ -26,30 +26,32 @@
 package sun.awt.X11;
 
 import java.awt.datatransfer.Transferable;
-
 import java.util.SortedMap;
-import java.util.Set;
-import java.util.Iterator;
-import java.util.HashSet;
-
 import java.io.IOException;
-
 import java.security.AccessController;
-
+import java.util.HashMap;
+import java.util.Map;
+import sun.awt.UNIXToolkit;
 import sun.awt.datatransfer.DataTransferer;
 import sun.awt.datatransfer.SunClipboard;
 import sun.awt.datatransfer.ClipboardTransferable;
-
 import sun.security.action.GetIntegerAction;
 
-
-
 /**
  * A class which interfaces with the X11 selection service in order to support
  * data transfer via Clipboard operations.
  */
-public class XClipboard extends SunClipboard implements Runnable {
+public final class XClipboard extends SunClipboard implements OwnershipListener
+{
     private final XSelection selection;
+    // Time of calling XConvertSelection().
+    private long convertSelectionTime;
+    // The flag used not to call XConvertSelection() if the previous SelectionNotify
+    // has not been processed by checkChange().
+    private volatile boolean isSelectionNotifyProcessed;
+    // The property in which the owner should place requested targets
+    // when tracking changes of available data flavors (practically targets).
+    private volatile XAtom targetsPropertyAtom;
 
     private static final Object classLock = new Object();
 
@@ -57,31 +59,33 @@
 
     private static int pollInterval;
 
-    private static Set listenedClipboards;
-
+    private static Map<Long, XClipboard> targetsAtom2Clipboard;
 
     /**
      * Creates a system clipboard object.
      */
     public XClipboard(String name, String selectionName) {
         super(name);
-        selection = new XSelection(XAtom.get(selectionName), this);
+        selection = new XSelection(XAtom.get(selectionName));
+        selection.registerOwershipListener(this);
     }
 
-    /**
-     * The action to be run when we lose ownership
+    /*
      * NOTE: This method may be called by privileged threads.
      *       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
      */
-    public void run() {
-        lostOwnershipImpl();
+    public void ownershipChanged(final boolean isOwner) {
+        if (isOwner) {
+            checkChangeHere(contents);
+        } else {
+            lostOwnershipImpl();
+        }
     }
 
     protected synchronized void setContentsNative(Transferable contents) {
         SortedMap formatMap = DataTransferer.getInstance().getFormatsForTransferable
                 (contents, DataTransferer.adaptFlavorMap(flavorMap));
-        long[] formats =
-            DataTransferer.getInstance().keysToLongArray(formatMap);
+        long[] formats = DataTransferer.keysToLongArray(formatMap);
 
         if (!selection.setOwner(contents, formatMap, formats,
                                 XToolkit.getCurrentServerTime())) {
@@ -94,6 +98,7 @@
         return selection.getSelectionAtom().getAtom();
     }
 
+    @Override
     public synchronized Transferable getContents(Object requestor) {
         if (contents != null) {
             return contents;
@@ -115,62 +120,163 @@
         return selection.getData(format, XToolkit.getCurrentServerTime());
     }
 
-    // Called on the toolkit thread under awtLock.
-    public void checkChange(long[] formats) {
-        if (!selection.isOwner()) {
-            super.checkChange(formats);
-        }
-    }
-
-    void checkChangeHere(Transferable contents) {
+    private void checkChangeHere(Transferable contents) {
         if (areFlavorListenersRegistered()) {
-            super.checkChange(DataTransferer.getInstance().
+            checkChange(DataTransferer.getInstance().
                         getFormatsForTransferableAsArray(contents, flavorMap));
         }
     }
 
+    private static int getPollInterval() {
+        synchronized (XClipboard.classLock) {
+            if (pollInterval <= 0) {
+                pollInterval = AccessController.doPrivileged(
+                        new GetIntegerAction("awt.datatransfer.clipboard.poll.interval",
+                                             defaultPollInterval));
+                if (pollInterval <= 0) {
+                    pollInterval = defaultPollInterval;
+                }
+            }
+            return pollInterval;
+        }
+    }
+
+    private XAtom getTargetsPropertyAtom() {
+        if (null == targetsPropertyAtom) {
+            targetsPropertyAtom =
+                    XAtom.get("XAWT_TARGETS_OF_SELECTION:" + selection.getSelectionAtom().getName());
+        }
+        return targetsPropertyAtom;
+    }
+
     protected void registerClipboardViewerChecked() {
-        if (pollInterval <= 0) {
-            pollInterval = ((Integer)AccessController.doPrivileged(
-                    new GetIntegerAction("awt.datatransfer.clipboard.poll.interval",
-                                         defaultPollInterval))).intValue();
-            if (pollInterval <= 0) {
-                pollInterval = defaultPollInterval;
+        // for XConvertSelection() to be called for the first time in getTargetsDelayed()
+        isSelectionNotifyProcessed = true;
+
+        boolean mustSchedule = false;
+        synchronized (XClipboard.classLock) {
+            if (targetsAtom2Clipboard == null) {
+                targetsAtom2Clipboard = new HashMap<Long, XClipboard>(2);
+            }
+            mustSchedule = targetsAtom2Clipboard.isEmpty();
+            targetsAtom2Clipboard.put(getTargetsPropertyAtom().getAtom(), this);
+            if (mustSchedule) {
+                XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
+                                            new SelectionNotifyHandler());
             }
         }
-        selection.initializeSelectionForTrackingChanges();
-        boolean mustSchedule = false;
-        synchronized (XClipboard.classLock) {
-            if (listenedClipboards == null) {
-                listenedClipboards = new HashSet(2);
-            }
-            mustSchedule = listenedClipboards.isEmpty();
-            listenedClipboards.add(this);
-        }
         if (mustSchedule) {
-            XToolkit.schedule(new CheckChangeTimerTask(), pollInterval);
+            XToolkit.schedule(new CheckChangeTimerTask(), XClipboard.getPollInterval());
         }
     }
 
     private static class CheckChangeTimerTask implements Runnable {
         public void run() {
-            for (Iterator iter = listenedClipboards.iterator(); iter.hasNext();) {
-                XClipboard clpbrd = (XClipboard)iter.next();
-                clpbrd.selection.getTargetsDelayed();
+            for (XClipboard clpbrd : targetsAtom2Clipboard.values()) {
+                clpbrd.getTargetsDelayed();
             }
             synchronized (XClipboard.classLock) {
-                if (listenedClipboards != null && !listenedClipboards.isEmpty()) {
-                    XToolkit.schedule(this, pollInterval);
+                if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) {
+                    XToolkit.schedule(this, XClipboard.getPollInterval());
+                }
+            }
+        }
+    }
+
+    private static class SelectionNotifyHandler implements XEventDispatcher {
+        public void dispatchEvent(XEvent ev) {
+            if (ev.get_type() == XlibWrapper.SelectionNotify) {
+                final XSelectionEvent xse = ev.get_xselection();
+                XClipboard clipboard = null;
+                synchronized (XClipboard.classLock) {
+                    if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) {
+                        XToolkit.removeEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), this);
+                        return;
+                    }
+                    final long propertyAtom = xse.get_property();
+                    clipboard = targetsAtom2Clipboard.get(propertyAtom);
+                }
+                if (null != clipboard) {
+                    clipboard.checkChange(xse);
                 }
             }
         }
     }
 
     protected void unregisterClipboardViewerChecked() {
-        selection.deinitializeSelectionForTrackingChanges();
+        isSelectionNotifyProcessed = false;
         synchronized (XClipboard.classLock) {
-            listenedClipboards.remove(this);
+            targetsAtom2Clipboard.remove(getTargetsPropertyAtom().getAtom());
+        }
+    }
+
+    // checkChange() will be called on SelectionNotify
+    private void getTargetsDelayed() {
+        XToolkit.awtLock();
+        try {
+            long curTime = System.currentTimeMillis();
+            if (isSelectionNotifyProcessed || curTime >= (convertSelectionTime + UNIXToolkit.getDatatransferTimeout()))
+            {
+                convertSelectionTime = curTime;
+                XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
+                                              selection.getSelectionAtom().getAtom(),
+                                              XDataTransferer.TARGETS_ATOM.getAtom(),
+                                              getTargetsPropertyAtom().getAtom(),
+                                              XWindow.getXAWTRootWindow().getWindow(),
+                                              XlibWrapper.CurrentTime);
+                isSelectionNotifyProcessed = false;
+            }
+        } finally {
+            XToolkit.awtUnlock();
         }
     }
 
+    /*
+     * Tracks changes of available formats.
+     * NOTE: This method may be called by privileged threads.
+     *       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
+     */
+    private void checkChange(XSelectionEvent xse) {
+        final long propertyAtom = xse.get_property();
+        if (propertyAtom != getTargetsPropertyAtom().getAtom()) {
+            // wrong atom
+            return;
+        }
+
+        final XAtom selectionAtom = XAtom.get(xse.get_selection());
+        final XSelection changedSelection = XSelection.getSelection(selectionAtom);
+
+        if (null == changedSelection || changedSelection != selection) {
+            // unknown selection - do nothing
+            return;
+        }
+
+        isSelectionNotifyProcessed = true;
+
+        if (selection.isOwner()) {
+            // selection is owner - do not need formats
+            return;
+        }
+
+        long[] formats = null;
+
+        if (propertyAtom == XlibWrapper.None) {
+            // We treat None property atom as "empty selection".
+            formats = new long[0];
+        } else {
+            WindowPropertyGetter targetsGetter =
+                new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
+                                         XAtom.get(propertyAtom), 0,
+                                         XSelection.MAX_LENGTH, true,
+                                         XlibWrapper.AnyPropertyType);
+            try {
+                targetsGetter.execute();
+                formats = XSelection.getFormats(targetsGetter);
+            } finally {
+                targetsGetter.dispose();
+            }
+        }
+
+        checkChange(formats);
+    }
 }