6538066: XSelection should be more passive
authorson
Thu, 13 Mar 2008 17:14:44 +0300
changeset 117 766ae458aaf1
parent 116 9c43d9eb1029
child 118 35b334a806ab
6538066: XSelection should be more passive Summary: Now only XClipboard know about XSelection, and XSelection knows nothing about XClipboard. Reviewed-by: uta, denis
jdk/src/solaris/classes/sun/awt/X11/MotifDnDConstants.java
jdk/src/solaris/classes/sun/awt/X11/MotifDnDDropTargetProtocol.java
jdk/src/solaris/classes/sun/awt/X11/OwnershipListener.java
jdk/src/solaris/classes/sun/awt/X11/XClipboard.java
jdk/src/solaris/classes/sun/awt/X11/XDnDConstants.java
jdk/src/solaris/classes/sun/awt/X11/XSelection.java
--- a/jdk/src/solaris/classes/sun/awt/X11/MotifDnDConstants.java	Thu Mar 13 17:08:15 2008 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/MotifDnDConstants.java	Thu Mar 13 17:14:44 2008 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2005 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
@@ -39,6 +39,8 @@
  * @since 1.5
  */
 class MotifDnDConstants {
+    // utility class can not be instantiated
+    private MotifDnDConstants() {}
     // Note that offsets in all native structures below do not depend on the
     // architecture.
     private static final Unsafe unsafe = XlibWrapper.unsafe;
@@ -55,8 +57,7 @@
         XAtom.get("XmTRANSFER_SUCCESS");
     static final XAtom XA_XmTRANSFER_FAILURE =
         XAtom.get("XmTRANSFER_FAILURE");
-    static final XSelection MotifDnDSelection =
-        new XSelection(XA_MOTIF_ATOM_0, null);
+    static final XSelection MotifDnDSelection = new XSelection(XA_MOTIF_ATOM_0);
 
     public static final byte MOTIF_DND_PROTOCOL_VERSION = 0;
 
@@ -231,6 +232,9 @@
     }
 
     public static final class Swapper {
+        // utility class can not be instantiated
+        private Swapper() {}
+
         public static short swap(short s) {
             return (short)(((s & 0xFF00) >>> 8) | ((s & 0xFF) << 8));
         }
--- a/jdk/src/solaris/classes/sun/awt/X11/MotifDnDDropTargetProtocol.java	Thu Mar 13 17:08:15 2008 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/MotifDnDDropTargetProtocol.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
@@ -933,7 +933,7 @@
 
         XSelection selection = XSelection.getSelection(selectionAtom);
         if (selection == null) {
-            selection = new XSelection(selectionAtom, null);
+            selection = new XSelection(selectionAtom);
         }
 
         return selection.getData(format, time_stamp);
@@ -1056,7 +1056,7 @@
         // the original structure can be freed before this
         // SunDropTargetEvent is dispatched.
         if (xclient != null) {
-            int size = new XClientMessageEvent(nativeCtxt).getSize();
+            int size = XClientMessageEvent.getSize();
 
             nativeCtxt = unsafe.allocateMemory(size + 4 * Native.getLongSize());
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/awt/X11/OwnershipListener.java	Thu Mar 13 17:14:44 2008 +0300
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.awt.X11;
+
+interface OwnershipListener {
+    public void ownershipChanged(final boolean isOwner);
+}
--- 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);
+    }
 }
--- a/jdk/src/solaris/classes/sun/awt/X11/XDnDConstants.java	Thu Mar 13 17:08:15 2008 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XDnDConstants.java	Thu Mar 13 17:14:44 2008 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2004 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
@@ -48,8 +48,7 @@
     static final XAtom XA_XdndStatus     = XAtom.get("XdndStatus");
     static final XAtom XA_XdndFinished   = XAtom.get("XdndFinished");
 
-    static final XSelection XDnDSelection =
-        new XSelection(XA_XdndSelection, null);
+    static final XSelection XDnDSelection = new XSelection(XA_XdndSelection);
 
     public static final int XDND_MIN_PROTOCOL_VERSION = 3;
     public static final int XDND_PROTOCOL_VERSION     = 5;
--- a/jdk/src/solaris/classes/sun/awt/X11/XSelection.java	Thu Mar 13 17:08:15 2008 +0300
+++ b/jdk/src/solaris/classes/sun/awt/X11/XSelection.java	Thu Mar 13 17:14:44 2008 +0300
@@ -32,9 +32,6 @@
 
 import java.util.Hashtable;
 import java.util.Map;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Collections;
 
 import sun.awt.AppContext;
 import sun.awt.SunToolkit;
@@ -45,7 +42,7 @@
 /**
  * A class which interfaces with the X11 selection service.
  */
-public class XSelection {
+public final class XSelection {
 
     /* Maps atoms to XSelection instances. */
     private static final Hashtable<XAtom, XSelection> table = new Hashtable<XAtom, XSelection>();
@@ -69,8 +66,6 @@
             XToolkit.awtUnlock();
         }
     }
-    /* The selection timeout. */
-    private static long SELECTION_TIMEOUT = UNIXToolkit.getDatatransferTimeout();
 
     /* The PropertyNotify event handler for incremental data transfer. */
     private static final XEventDispatcher incrementalTransferHandler =
@@ -84,11 +79,6 @@
 
     /* The X atom for the underlying selection. */
     private final XAtom selectionAtom;
-    /*
-     * XClipboard.run() is to be called when we lose ownership.
-     * XClipbioard.checkChange() is to be called when tracking changes of flavors.
-     */
-    private final XClipboard clipboard;
 
     /*
      * Owner-related variables - protected with synchronized (this).
@@ -109,17 +99,8 @@
     private long ownershipTime = 0;
     // True if we are the owner of this selection.
     private boolean isOwner;
-    // The property in which the owner should place requested targets
-    // when tracking changes of available data flavors (practically targets).
-    private volatile XAtom targetsPropertyAtom;
-    // A set of these property atoms.
-    private static volatile Set targetsPropertyAtoms;
-    // The flag used not to call XConvertSelection() if the previous SelectionNotify
-    // has not been processed by checkChange().
-    private volatile boolean isSelectionNotifyProcessed;
-    // Time of calling XConvertSelection().
-    private long convertSelectionTime;
-
+    private OwnershipListener ownershipListener = null;
+    private final Object stateLock = new Object();
 
     static {
         XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
@@ -141,12 +122,11 @@
      * @param clpbrd the corresponding clipoboard
      * @exception NullPointerException if atom is <code>null</code>.
      */
-    public XSelection(XAtom atom, XClipboard clpbrd) {
+    public XSelection(XAtom atom) {
         if (atom == null) {
             throw new NullPointerException("Null atom");
         }
         selectionAtom = atom;
-        clipboard = clpbrd;
         table.put(selectionAtom, this);
     }
 
@@ -154,25 +134,9 @@
         return selectionAtom;
     }
 
-    void initializeSelectionForTrackingChanges() {
-        targetsPropertyAtom = XAtom.get("XAWT_TARGETS_OF_SELECTION:" + selectionAtom.getName());
-        if (targetsPropertyAtoms == null) {
-            targetsPropertyAtoms = Collections.synchronizedSet(new HashSet(2));
-        }
-        targetsPropertyAtoms.add(Long.valueOf(targetsPropertyAtom.getAtom()));
-        // for XConvertSelection() to be called for the first time in getTargetsDelayed()
-        isSelectionNotifyProcessed = true;
-    }
-
-    void deinitializeSelectionForTrackingChanges() {
-        if (targetsPropertyAtoms != null && targetsPropertyAtom != null) {
-            targetsPropertyAtoms.remove(Long.valueOf(targetsPropertyAtom.getAtom()));
-        }
-        isSelectionNotifyProcessed = false;
-    }
-
     public synchronized boolean setOwner(Transferable contents, Map formatMap,
-                                         long[] formats, long time) {
+                                         long[] formats, long time)
+    {
         long owner = XWindow.getXAWTRootWindow().getWindow();
         long selection = selectionAtom.getAtom();
 
@@ -192,15 +156,12 @@
             XlibWrapper.XSetSelectionOwner(XToolkit.getDisplay(),
                                            selection, owner, time);
             if (XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
-                                               selection) != owner) {
-
+                                               selection) != owner)
+            {
                 reset();
                 return false;
             }
-            isOwner = true;
-            if (clipboard != null) {
-                clipboard.checkChangeHere(contents);
-            }
+            setOwnerProp(true);
             return true;
         } finally {
             XToolkit.awtUnlock();
@@ -217,7 +178,7 @@
             do {
                 DataTransferer.getInstance().processDataConversionRequests();
                 XToolkit.awtLockWait(250);
-            } while (propertyGetter == dataGetter && System.currentTimeMillis() < startTime + SELECTION_TIMEOUT);
+            } while (propertyGetter == dataGetter && System.currentTimeMillis() < startTime + UNIXToolkit.getDatatransferTimeout());
         } finally {
             XToolkit.awtUnlock();
         }
@@ -232,11 +193,9 @@
             throw new Error("UNIMPLEMENTED");
         }
 
-        long[] formats = null;
+        long[] targets = null;
 
         synchronized (lock) {
-            SELECTION_TIMEOUT = UNIXToolkit.getDatatransferTimeout();
-
             WindowPropertyGetter targetsGetter =
                 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
                                          selectionPropertyAtom, 0, MAX_LENGTH,
@@ -267,15 +226,15 @@
                 } finally {
                     XToolkit.awtUnlock();
                 }
-                formats = getFormats(targetsGetter);
+                targets = getFormats(targetsGetter);
             } finally {
                 targetsGetter.dispose();
             }
         }
-        return formats;
+        return targets;
     }
 
-    private static long[] getFormats(WindowPropertyGetter targetsGetter) {
+    static long[] getFormats(WindowPropertyGetter targetsGetter) {
         long[] formats = null;
 
         if (targetsGetter.isExecuted() && !targetsGetter.isDisposed() &&
@@ -285,7 +244,7 @@
         {
             // we accept property with TARGETS type to be compatible with old jdks
             // see 6607163
-            int count = (int)targetsGetter.getNumberOfItems();
+            int count = targetsGetter.getNumberOfItems();
             if (count > 0) {
                 long atoms = targetsGetter.getData();
                 formats = new long[count];
@@ -299,26 +258,6 @@
         return formats != null ? formats : new long[0];
     }
 
-    // checkChange() will be called on SelectionNotify
-    void getTargetsDelayed() {
-        XToolkit.awtLock();
-        try {
-            long curTime = System.currentTimeMillis();
-            if (isSelectionNotifyProcessed || curTime >= convertSelectionTime + SELECTION_TIMEOUT) {
-                convertSelectionTime = curTime;
-                XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
-                                              getSelectionAtom().getAtom(),
-                                              XDataTransferer.TARGETS_ATOM.getAtom(),
-                                              targetsPropertyAtom.getAtom(),
-                                              XWindow.getXAWTRootWindow().getWindow(),
-                                              XlibWrapper.CurrentTime);
-                isSelectionNotifyProcessed = false;
-            }
-        } finally {
-            XToolkit.awtUnlock();
-        }
-    }
-
     /*
      * Requests the selection data in the specified format and returns
      * the data provided by the owner.
@@ -331,8 +270,6 @@
         byte[] data = null;
 
         synchronized (lock) {
-            SELECTION_TIMEOUT = UNIXToolkit.getDatatransferTimeout();
-
             WindowPropertyGetter dataGetter =
                 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
                                          selectionPropertyAtom, 0, MAX_LENGTH,
@@ -381,7 +318,7 @@
                                               dataGetter.getActualFormat());
                     }
 
-                    int count = (int)dataGetter.getNumberOfItems();
+                    int count = dataGetter.getNumberOfItems();
 
                     if (count <= 0) {
                         throw new IOException("INCR data is missed.");
@@ -457,7 +394,7 @@
                                                       incrDataGetter.getActualFormat());
                             }
 
-                            count = (int)incrDataGetter.getNumberOfItems();
+                            count = incrDataGetter.getNumberOfItems();
 
                             if (count == 0) {
                                 break;
@@ -491,7 +428,7 @@
                                               dataGetter.getActualFormat());
                     }
 
-                    int count = (int)dataGetter.getNumberOfItems();
+                    int count = dataGetter.getNumberOfItems();
                     if (count > 0) {
                         data = new byte[count];
                         long ptr = dataGetter.getData();
@@ -513,11 +450,14 @@
         return isOwner;
     }
 
-    public void lostOwnership() {
-        isOwner = false;
-        if (clipboard != null) {
-            clipboard.run();
-        }
+    // To be MT-safe this method should be called under awtLock.
+    private void setOwnerProp(boolean f) {
+        isOwner = f;
+        fireOwnershipChanges(isOwner);
+    }
+
+    private void lostOwnership() {
+        setOwnerProp(false);
     }
 
     public synchronized void reset() {
@@ -597,125 +537,39 @@
 
     private void handleSelectionRequest(XSelectionRequestEvent xsre) {
         long property = xsre.get_property();
-        long requestor = xsre.get_requestor();
-        long requestTime = xsre.get_time();
-        long format = xsre.get_target();
-        int dataFormat = 0;
+        final long requestor = xsre.get_requestor();
+        final long requestTime = xsre.get_time();
+        final long format = xsre.get_target();
         boolean conversionSucceeded = false;
 
         if (ownershipTime != 0 &&
-            (requestTime == XlibWrapper.CurrentTime ||
-             requestTime >= ownershipTime)) {
-
-            property = xsre.get_property();
-
+            (requestTime == XlibWrapper.CurrentTime || requestTime >= ownershipTime))
+        {
             // Handle MULTIPLE requests as per ICCCM.
             if (format == XDataTransferer.MULTIPLE_ATOM.getAtom()) {
-                // The property cannot be 0 for a MULTIPLE request.
-                if (property != 0) {
-                    // First retrieve the list of requested targets.
-                    WindowPropertyGetter wpg =
-                        new WindowPropertyGetter(requestor, XAtom.get(property), 0,
-                                                 MAX_LENGTH, false,
-                                                 XlibWrapper.AnyPropertyType);
-                    try {
-                        wpg.execute();
-
-                        if (wpg.getActualFormat() == 32 &&
-                            (wpg.getNumberOfItems() % 2) == 0) {
-                            long count = wpg.getNumberOfItems() / 2;
-                            long pairsPtr = wpg.getData();
-                            boolean writeBack = false;
-                            for (int i = 0; i < count; i++) {
-                                long target = Native.getLong(pairsPtr, 2*i);
-                                long prop = Native.getLong(pairsPtr, 2*i + 1);
-
-                                if (!convertAndStore(requestor, target, prop)) {
-                                    // To report failure, we should replace the
-                                    // target atom with 0 in the MULTIPLE property.
-                                    Native.putLong(pairsPtr, 2*i, 0);
-                                    writeBack = true;
-                                }
-                            }
-                            if (writeBack) {
-                                XToolkit.awtLock();
-                                try {
-                                    XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor,
-                                                                property,
-                                                                wpg.getActualType(),
-                                                                wpg.getActualFormat(),
-                                                                XlibWrapper.PropModeReplace,
-                                                                wpg.getData(),
-                                                                wpg.getNumberOfItems());
-                                } finally {
-                                    XToolkit.awtUnlock();
-                                }
-                            }
-                            conversionSucceeded = true;
-                        }
-                    } finally {
-                        wpg.dispose();
-                    }
-                }
+                conversionSucceeded = handleMultipleRequest(requestor, property);
             } else {
-
                 // Support for obsolete clients as per ICCCM.
-                if (property == 0) {
+                if (property == XlibWrapper.None) {
                     property = format;
                 }
 
                 if (format == XDataTransferer.TARGETS_ATOM.getAtom()) {
-                    long nativeDataPtr = 0;
-                    int count = 0;
-                    dataFormat = 32;
-
-                    // Use a local copy to avoid synchronization.
-                    long[] formatsLocal = formats;
-
-                    if (formatsLocal == null) {
-                        throw new IllegalStateException("Not an owner.");
-                    }
-
-                    count = formatsLocal.length;
-
-                    try {
-                        if (count > 0) {
-                            nativeDataPtr = Native.allocateLongArray(count);
-                            Native.put(nativeDataPtr, formatsLocal);
-                        }
-
-                        conversionSucceeded = true;
-
-                        XToolkit.awtLock();
-                        try {
-                            XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor,
-                                                        property, XAtom.XA_ATOM, dataFormat,
-                                                        XlibWrapper.PropModeReplace,
-                                                        nativeDataPtr, count);
-                        } finally {
-                            XToolkit.awtUnlock();
-                        }
-                    } finally {
-                        if (nativeDataPtr != 0) {
-                            XlibWrapper.unsafe.freeMemory(nativeDataPtr);
-                            nativeDataPtr = 0;
-                        }
-                    }
+                    conversionSucceeded = handleTargetsRequest(property, requestor);
                 } else {
-                    conversionSucceeded = convertAndStore(requestor, format,
-                                                          property);
+                    conversionSucceeded = convertAndStore(requestor, format, property);
                 }
             }
         }
 
         if (!conversionSucceeded) {
-            // Zero property indicates conversion failure.
-            property = 0;
+            // None property indicates conversion failure.
+            property = XlibWrapper.None;
         }
 
         XSelectionEvent xse = new XSelectionEvent();
         try {
-            xse.set_type((int)XlibWrapper.SelectionNotify);
+            xse.set_type(XlibWrapper.SelectionNotify);
             xse.set_send_event(true);
             xse.set_requestor(requestor);
             xse.set_selection(selectionAtom.getAtom());
@@ -735,40 +589,123 @@
         }
     }
 
-    private static void checkChange(XSelectionEvent xse) {
-        if (targetsPropertyAtoms == null || targetsPropertyAtoms.isEmpty()) {
-            // We are not tracking changes.
-            return;
+    private boolean handleMultipleRequest(final long requestor, long property) {
+        if (XlibWrapper.None == property) {
+            // The property cannot be None for a MULTIPLE request.
+            return false;
+        }
+
+        boolean conversionSucceeded = false;
+
+        // First retrieve the list of requested targets.
+        WindowPropertyGetter wpg =
+                new WindowPropertyGetter(requestor, XAtom.get(property),
+                                         0, MAX_LENGTH, false,
+                                         XlibWrapper.AnyPropertyType);
+        try {
+            wpg.execute();
+
+            if (wpg.getActualFormat() == 32 && (wpg.getNumberOfItems() % 2) == 0) {
+                final long count = wpg.getNumberOfItems() / 2;
+                final long pairsPtr = wpg.getData();
+                boolean writeBack = false;
+
+                for (int i = 0; i < count; i++) {
+                    long target = Native.getLong(pairsPtr, 2 * i);
+                    long prop = Native.getLong(pairsPtr, 2 * i + 1);
+
+                    if (!convertAndStore(requestor, target, prop)) {
+                        // To report failure, we should replace the
+                        // target atom with 0 in the MULTIPLE property.
+                        Native.putLong(pairsPtr, 2 * i, 0);
+                        writeBack = true;
+                    }
+                }
+                if (writeBack) {
+                    XToolkit.awtLock();
+                    try {
+                        XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
+                                                    requestor,
+                                                    property,
+                                                    wpg.getActualType(),
+                                                    wpg.getActualFormat(),
+                                                    XlibWrapper.PropModeReplace,
+                                                    wpg.getData(),
+                                                    wpg.getNumberOfItems());
+                    } finally {
+                        XToolkit.awtUnlock();
+                    }
+                }
+                conversionSucceeded = true;
+            }
+        } finally {
+            wpg.dispose();
         }
 
-        long propertyAtom = xse.get_property();
-        long[] formats = null;
+        return conversionSucceeded;
+    }
+
+    private boolean handleTargetsRequest(long property, long requestor)
+            throws IllegalStateException
+    {
+        boolean conversionSucceeded = false;
+        // Use a local copy to avoid synchronization.
+        long[] formatsLocal = formats;
+
+        if (formatsLocal == null) {
+            throw new IllegalStateException("Not an owner.");
+        }
+
+        long nativeDataPtr = 0;
+
+        try {
+            final int count = formatsLocal.length;
+            final int dataFormat = 32;
 
-        if (propertyAtom == XlibWrapper.None) {
-            // We threat None property atom as "empty selection".
-            formats = new long[0];
-        } else if (!targetsPropertyAtoms.contains(Long.valueOf(propertyAtom))) {
-            return;
-        } else {
-            WindowPropertyGetter targetsGetter =
-                new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
-                                         XAtom.get(propertyAtom), 0, MAX_LENGTH,
-                                         true, XlibWrapper.AnyPropertyType);
+            if (count > 0) {
+                nativeDataPtr = Native.allocateLongArray(count);
+                Native.put(nativeDataPtr, formatsLocal);
+            }
+
+            conversionSucceeded = true;
+
+            XToolkit.awtLock();
             try {
-                targetsGetter.execute();
-                formats = getFormats(targetsGetter);
+                XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor,
+                                            property, XAtom.XA_ATOM, dataFormat,
+                                            XlibWrapper.PropModeReplace,
+                                            nativeDataPtr, count);
             } finally {
-                targetsGetter.dispose();
+                XToolkit.awtUnlock();
+            }
+        } finally {
+            if (nativeDataPtr != 0) {
+                XlibWrapper.unsafe.freeMemory(nativeDataPtr);
+                nativeDataPtr = 0;
             }
         }
+        return conversionSucceeded;
+    }
 
-        XAtom selectionAtom = XAtom.get(xse.get_selection());
-        XSelection selection = getSelection(selectionAtom);
-        if (selection != null) {
-            selection.isSelectionNotifyProcessed = true;
-            if (selection.clipboard != null) {
-                selection.clipboard.checkChange(formats);
-            }
+    private void fireOwnershipChanges(final boolean isOwner) {
+        OwnershipListener l = null;
+        synchronized (stateLock) {
+            l = ownershipListener;
+        }
+        if (null != l) {
+            l.ownershipChanged(isOwner);
+        }
+    }
+
+    void registerOwershipListener(OwnershipListener l) {
+        synchronized (stateLock) {
+            ownershipListener = l;
+        }
+    }
+
+    void unregisterOwnershipListener() {
+        synchronized (stateLock) {
+            ownershipListener = null;
         }
     }
 
@@ -776,10 +713,9 @@
         public void dispatchEvent(XEvent ev) {
             switch (ev.get_type()) {
             case XlibWrapper.SelectionNotify: {
-                XSelectionEvent xse = ev.get_xselection();
-                checkChange(xse);
                 XToolkit.awtLock();
                 try {
+                    XSelectionEvent xse = ev.get_xselection();
                     // Ignore the SelectionNotify event if it is not the response to our last request.
                     if (propertyGetter != null && xse.get_time() == lastRequestServerTime) {
                         // The property will be None in case of convertion failure.