1 /*
2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.awt.X11;
27 
28 import java.awt.datatransfer.DataFlavor;
29 import java.awt.datatransfer.Transferable;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.io.IOException;
33 
34 import java.util.Hashtable;
35 import java.util.Map;
36 
37 import sun.awt.AppContext;
38 import sun.awt.SunToolkit;
39 import sun.awt.UNIXToolkit;
40 
41 import sun.awt.datatransfer.DataTransferer;
42 
43 /**
44  * A class which interfaces with the X11 selection service.
45  */
46 final class XSelection {
47 
48     /* Maps atoms to XSelection instances. */
49     private static final Hashtable<XAtom, XSelection> table = new Hashtable<XAtom, XSelection>();
50     /* Prevents from parallel selection data request processing. */
51     private static final Object lock = new Object();
52     /* The property in which the owner should place the requested data. */
53     private static final XAtom selectionPropertyAtom = XAtom.get("XAWT_SELECTION");
54     /* The maximal length of the property data. */
55     public static final long MAX_LENGTH = 1000000;
56     /*
57      * The maximum data size for ChangeProperty request.
58      * 100 is for the structure prepended to the request.
59      */
60     public static final int MAX_PROPERTY_SIZE;
61     static {
XToolkit.awtLock()62         XToolkit.awtLock();
63         try {
64             MAX_PROPERTY_SIZE =
65                 (int)(XlibWrapper.XMaxRequestSize(XToolkit.getDisplay()) * 4 - 100);
66         } finally {
XToolkit.awtUnlock()67             XToolkit.awtUnlock();
68         }
69     }
70 
71     /* The PropertyNotify event handler for incremental data transfer. */
72     private static final XEventDispatcher incrementalTransferHandler =
73         new IncrementalTransferHandler();
74     /* The context for the current request - protected with awtLock. */
75     private static WindowPropertyGetter propertyGetter = null;
76 
77     // The orders of the lock acquisition:
78     //   XClipboard -> XSelection -> awtLock.
79     //   lock -> awtLock.
80 
81     /* The X atom for the underlying selection. */
82     private final XAtom selectionAtom;
83 
84     /*
85      * Owner-related variables - protected with synchronized (this).
86      */
87 
88     /* The contents supplied by the current owner. */
89     private Transferable contents = null;
90     /* The format-to-flavor map for the current owner. */
91     private Map<Long, DataFlavor> formatMap = null;
92     /* The formats supported by the current owner was set. */
93     private long[] formats = null;
94     /* The AppContext in which the current owner was set. */
95     private AppContext appContext = null;
96     // The X server time of the last XConvertSelection() call;
97     // protected with 'lock' and awtLock.
98     private static long lastRequestServerTime;
99     /* The time at which the current owner was set. */
100     private long ownershipTime = 0;
101     // True if we are the owner of this selection.
102     private boolean isOwner;
103     private OwnershipListener ownershipListener = null;
104     private final Object stateLock = new Object();
105 
106     static {
107         XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
SelectionEventHandler()108                                     new SelectionEventHandler());
109     }
110 
111     /*
112      * Returns the XSelection object for the specified selection atom or
113      * {@code null} if none exists.
114      */
getSelection(XAtom atom)115     static XSelection getSelection(XAtom atom) {
116         return table.get(atom);
117     }
118 
119     /**
120      * Creates a selection object.
121      *
122      * @param  atom the selection atom
123      * @throws NullPointerException if atom is {@code null}
124      */
XSelection(XAtom atom)125     XSelection(XAtom atom) {
126         if (atom == null) {
127             throw new NullPointerException("Null atom");
128         }
129         selectionAtom = atom;
130         table.put(selectionAtom, this);
131     }
132 
getSelectionAtom()133     public XAtom getSelectionAtom() {
134         return selectionAtom;
135     }
136 
setOwner(Transferable contents, Map<Long, DataFlavor> formatMap, long[] formats, long time)137     synchronized boolean setOwner(Transferable contents,
138                                   Map<Long, DataFlavor> formatMap,
139                                   long[] formats, long time) {
140         long owner = XWindow.getXAWTRootWindow().getWindow();
141         long selection = selectionAtom.getAtom();
142 
143         // ICCCM prescribes that CurrentTime should not be used for SetSelectionOwner.
144         if (time == XConstants.CurrentTime) {
145             time = XToolkit.getCurrentServerTime();
146         }
147 
148         this.contents = contents;
149         this.formatMap = formatMap;
150         this.formats = formats;
151         this.appContext = AppContext.getAppContext();
152         this.ownershipTime = time;
153 
154         XToolkit.awtLock();
155         try {
156             XlibWrapper.XSetSelectionOwner(XToolkit.getDisplay(),
157                                            selection, owner, time);
158             if (XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
159                                                selection) != owner)
160             {
161                 reset();
162                 return false;
163             }
164             setOwnerProp(true);
165             return true;
166         } finally {
167             XToolkit.awtUnlock();
168         }
169     }
170 
171     /**
172      * Blocks the current thread till SelectionNotify or PropertyNotify (in case of INCR transfer) arrives.
173      */
waitForSelectionNotify(WindowPropertyGetter dataGetter)174     private static void waitForSelectionNotify(WindowPropertyGetter dataGetter) throws InterruptedException {
175         long startTime = System.currentTimeMillis();
176         XToolkit.awtLock();
177         try {
178             do {
179                 DataTransferer.getInstance().processDataConversionRequests();
180                 XToolkit.awtLockWait(250);
181             } while (propertyGetter == dataGetter && System.currentTimeMillis() < startTime + UNIXToolkit.getDatatransferTimeout());
182         } finally {
183             XToolkit.awtUnlock();
184         }
185     }
186 
187     /*
188      * Returns the list of atoms that represent the targets for which an attempt
189      * to convert the current selection will succeed.
190      */
getTargets(long time)191     public long[] getTargets(long time) {
192         if (XToolkit.isToolkitThread()) {
193             throw new Error("UNIMPLEMENTED");
194         }
195 
196         long[] targets = null;
197 
198         synchronized (lock) {
199             WindowPropertyGetter targetsGetter =
200                 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
201                                          selectionPropertyAtom, 0, MAX_LENGTH,
202                                          true, XConstants.AnyPropertyType);
203 
204             try {
205                 XToolkit.awtLock();
206                 try {
207                     propertyGetter = targetsGetter;
208                     lastRequestServerTime = time;
209 
210                     XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
211                                                   getSelectionAtom().getAtom(),
212                                                   XDataTransferer.TARGETS_ATOM.getAtom(),
213                                                   selectionPropertyAtom.getAtom(),
214                                                   XWindow.getXAWTRootWindow().getWindow(),
215                                                   time);
216 
217                     // If the owner doesn't respond within the
218                     // SELECTION_TIMEOUT, we report conversion failure.
219                     try {
220                         waitForSelectionNotify(targetsGetter);
221                     } catch (InterruptedException ie) {
222                         return new long[0];
223                     } finally {
224                         propertyGetter = null;
225                     }
226                 } finally {
227                     XToolkit.awtUnlock();
228                 }
229                 targets = getFormats(targetsGetter);
230             } finally {
231                 targetsGetter.dispose();
232             }
233         }
234         return targets;
235     }
236 
getFormats(WindowPropertyGetter targetsGetter)237     static long[] getFormats(WindowPropertyGetter targetsGetter) {
238         long[] formats = null;
239 
240         if (targetsGetter.isExecuted() && !targetsGetter.isDisposed() &&
241                 (targetsGetter.getActualType() == XAtom.XA_ATOM ||
242                  targetsGetter.getActualType() == XDataTransferer.TARGETS_ATOM.getAtom()) &&
243                 targetsGetter.getActualFormat() == 32)
244         {
245             // we accept property with TARGETS type to be compatible with old jdks
246             // see 6607163
247             int count = targetsGetter.getNumberOfItems();
248             if (count > 0) {
249                 long atoms = targetsGetter.getData();
250                 formats = new long[count];
251                 for (int index = 0; index < count; index++) {
252                     formats[index] =
253                             Native.getLong(atoms+index*XAtom.getAtomSize());
254                 }
255             }
256         }
257 
258         return formats != null ? formats : new long[0];
259     }
260 
261     /*
262      * Requests the selection data in the specified format and returns
263      * the data provided by the owner.
264      */
getData(long format, long time)265     public byte[] getData(long format, long time) throws IOException {
266         if (XToolkit.isToolkitThread()) {
267             throw new Error("UNIMPLEMENTED");
268         }
269 
270         byte[] data = null;
271 
272         synchronized (lock) {
273             WindowPropertyGetter dataGetter =
274                 new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
275                                          selectionPropertyAtom, 0, MAX_LENGTH,
276                                          false, // don't delete to handle INCR properly.
277                                          XConstants.AnyPropertyType);
278 
279             try {
280                 XToolkit.awtLock();
281                 try {
282                     propertyGetter = dataGetter;
283                     lastRequestServerTime = time;
284 
285                     XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
286                                                   getSelectionAtom().getAtom(),
287                                                   format,
288                                                   selectionPropertyAtom.getAtom(),
289                                                   XWindow.getXAWTRootWindow().getWindow(),
290                                                   time);
291 
292                     // If the owner doesn't respond within the
293                     // SELECTION_TIMEOUT, we report conversion failure.
294                     try {
295                         waitForSelectionNotify(dataGetter);
296                     } catch (InterruptedException ie) {
297                         return new byte[0];
298                     } finally {
299                         propertyGetter = null;
300                     }
301                 } finally {
302                     XToolkit.awtUnlock();
303                 }
304 
305                 validateDataGetter(dataGetter);
306 
307                 // Handle incremental transfer.
308                 if (dataGetter.getActualType() ==
309                     XDataTransferer.INCR_ATOM.getAtom()) {
310 
311                     if (dataGetter.getActualFormat() != 32) {
312                         throw new IOException("Unsupported INCR format: " +
313                                               dataGetter.getActualFormat());
314                     }
315 
316                     int count = dataGetter.getNumberOfItems();
317 
318                     if (count <= 0) {
319                         throw new IOException("INCR data is missed.");
320                     }
321 
322                     long ptr = dataGetter.getData();
323 
324                     int len = 0;
325 
326                     {
327                         // Following Xt sources use the last element.
328                         long longLength = Native.getLong(ptr, count-1);
329 
330                         if (longLength <= 0) {
331                             return new byte[0];
332                         }
333 
334                         if (longLength > Integer.MAX_VALUE) {
335                             throw new IOException("Can't handle large data block: "
336                                                   + longLength + " bytes");
337                         }
338 
339                         len = (int)longLength;
340                     }
341 
342                     dataGetter.dispose();
343 
344                     ByteArrayOutputStream dataStream = new ByteArrayOutputStream(len);
345 
346                     while (true) {
347                         WindowPropertyGetter incrDataGetter =
348                             new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
349                                                      selectionPropertyAtom,
350                                                      0, MAX_LENGTH, false,
351                                                      XConstants.AnyPropertyType);
352 
353                         try {
354                             XToolkit.awtLock();
355                             XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
356                                                         incrementalTransferHandler);
357 
358                             propertyGetter = incrDataGetter;
359 
360                             try {
361                                 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
362                                                             XWindow.getXAWTRootWindow().getWindow(),
363                                                             selectionPropertyAtom.getAtom());
364 
365                                 // If the owner doesn't respond within the
366                                 // SELECTION_TIMEOUT, we terminate incremental
367                                 // transfer.
368                                 waitForSelectionNotify(incrDataGetter);
369                             } catch (InterruptedException ie) {
370                                 break;
371                             } finally {
372                                 propertyGetter = null;
373                                 XToolkit.removeEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
374                                                                incrementalTransferHandler);
375                                 XToolkit.awtUnlock();
376                             }
377 
378                             validateDataGetter(incrDataGetter);
379 
380                             if (incrDataGetter.getActualFormat() != 8) {
381                                 throw new IOException("Unsupported data format: " +
382                                                       incrDataGetter.getActualFormat());
383                             }
384 
385                             count = incrDataGetter.getNumberOfItems();
386 
387                             if (count == 0) {
388                                 break;
389                             }
390 
391                             if (count > 0) {
392                                 ptr = incrDataGetter.getData();
393                                 for (int index = 0; index < count; index++) {
394                                     dataStream.write(Native.getByte(ptr + index));
395                                 }
396                             }
397 
398                             data = dataStream.toByteArray();
399 
400                         } finally {
401                             incrDataGetter.dispose();
402                         }
403                     }
404                 } else {
405                     XToolkit.awtLock();
406                     try {
407                         XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
408                                                     XWindow.getXAWTRootWindow().getWindow(),
409                                                     selectionPropertyAtom.getAtom());
410                     } finally {
411                         XToolkit.awtUnlock();
412                     }
413 
414                     if (dataGetter.getActualFormat() != 8) {
415                         throw new IOException("Unsupported data format: " +
416                                               dataGetter.getActualFormat());
417                     }
418 
419                     int count = dataGetter.getNumberOfItems();
420                     if (count > 0) {
421                         data = new byte[count];
422                         long ptr = dataGetter.getData();
423                         for (int index = 0; index < count; index++) {
424                             data[index] = Native.getByte(ptr + index);
425                         }
426                     }
427                 }
428             } finally {
429                 dataGetter.dispose();
430             }
431         }
432 
433         return data != null ? data : new byte[0];
434     }
435 
validateDataGetter(WindowPropertyGetter propertyGetter)436     private void validateDataGetter(WindowPropertyGetter propertyGetter)
437             throws IOException
438     {
439         // The order of checks is important because a property getter
440         // has not been executed in case of timeout as well as in case of
441         // changed selection owner.
442 
443         if (propertyGetter.isDisposed()) {
444             throw new IOException("Owner failed to convert data");
445         }
446 
447         // The owner didn't respond - terminate the transfer.
448         if (!propertyGetter.isExecuted()) {
449             throw new IOException("Owner timed out");
450         }
451     }
452 
453     // To be MT-safe this method should be called under awtLock.
isOwner()454     boolean isOwner() {
455         return isOwner;
456     }
457 
458     // To be MT-safe this method should be called under awtLock.
setOwnerProp(boolean f)459     private void setOwnerProp(boolean f) {
460         isOwner = f;
461         fireOwnershipChanges(isOwner);
462     }
463 
lostOwnership()464     private void lostOwnership() {
465         setOwnerProp(false);
466     }
467 
reset()468     public synchronized void reset() {
469         contents = null;
470         formatMap = null;
471         formats = null;
472         appContext = null;
473         ownershipTime = 0;
474     }
475 
476     // Converts the data to the 'format' and if the conversion succeeded stores
477     // the data in the 'property' on the 'requestor' window.
478     // Returns true if the conversion succeeded.
convertAndStore(long requestor, long format, long property)479     private boolean convertAndStore(long requestor, long format, long property) {
480         int dataFormat = 8; /* Can choose between 8,16,32. */
481         byte[] byteData = null;
482         long nativeDataPtr = 0;
483         int count = 0;
484 
485         try {
486             SunToolkit.insertTargetMapping(this, appContext);
487 
488             byteData = DataTransferer.getInstance().convertData(this,
489                                                                 contents,
490                                                                 format,
491                                                                 formatMap,
492                                                                 XToolkit.isToolkitThread());
493         } catch (IOException ioe) {
494             return false;
495         }
496 
497         if (byteData == null) {
498             return false;
499         }
500 
501         count = byteData.length;
502 
503         try {
504             if (count > 0) {
505                 if (count <= MAX_PROPERTY_SIZE) {
506                     nativeDataPtr = Native.toData(byteData);
507                 } else {
508                     // Initiate incremental data transfer.
509                     new IncrementalDataProvider(requestor, property, format, 8,
510                                                 byteData);
511 
512                     nativeDataPtr =
513                         XlibWrapper.unsafe.allocateMemory(XAtom.getAtomSize());
514 
515                     Native.putLong(nativeDataPtr, (long)count);
516 
517                     format = XDataTransferer.INCR_ATOM.getAtom();
518                     dataFormat = 32;
519                     count = 1;
520                 }
521 
522             }
523 
524             XToolkit.awtLock();
525             try {
526                 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor, property,
527                                             format, dataFormat,
528                                             XConstants.PropModeReplace,
529                                             nativeDataPtr, count);
530             } finally {
531                 XToolkit.awtUnlock();
532             }
533         } finally {
534             if (nativeDataPtr != 0) {
535                 XlibWrapper.unsafe.freeMemory(nativeDataPtr);
536                 nativeDataPtr = 0;
537             }
538         }
539 
540         return true;
541     }
542 
handleSelectionRequest(XSelectionRequestEvent xsre)543     private void handleSelectionRequest(XSelectionRequestEvent xsre) {
544         long property = xsre.get_property();
545         final long requestor = xsre.get_requestor();
546         final long requestTime = xsre.get_time();
547         final long format = xsre.get_target();
548         boolean conversionSucceeded = false;
549 
550         if (ownershipTime != 0 &&
551             (requestTime == XConstants.CurrentTime || requestTime >= ownershipTime))
552         {
553             // Handle MULTIPLE requests as per ICCCM.
554             if (format == XDataTransferer.MULTIPLE_ATOM.getAtom()) {
555                 conversionSucceeded = handleMultipleRequest(requestor, property);
556             } else {
557                 // Support for obsolete clients as per ICCCM.
558                 if (property == XConstants.None) {
559                     property = format;
560                 }
561 
562                 if (format == XDataTransferer.TARGETS_ATOM.getAtom()) {
563                     conversionSucceeded = handleTargetsRequest(property, requestor);
564                 } else {
565                     conversionSucceeded = convertAndStore(requestor, format, property);
566                 }
567             }
568         }
569 
570         if (!conversionSucceeded) {
571             // None property indicates conversion failure.
572             property = XConstants.None;
573         }
574 
575         XSelectionEvent xse = new XSelectionEvent();
576         try {
577             xse.set_type(XConstants.SelectionNotify);
578             xse.set_send_event(true);
579             xse.set_requestor(requestor);
580             xse.set_selection(selectionAtom.getAtom());
581             xse.set_target(format);
582             xse.set_property(property);
583             xse.set_time(requestTime);
584 
585             XToolkit.awtLock();
586             try {
587                 XlibWrapper.XSendEvent(XToolkit.getDisplay(), requestor, false,
588                                        XConstants.NoEventMask, xse.pData);
589             } finally {
590                 XToolkit.awtUnlock();
591             }
592         } finally {
593             xse.dispose();
594         }
595     }
596 
handleMultipleRequest(final long requestor, long property)597     private boolean handleMultipleRequest(final long requestor, long property) {
598         if (XConstants.None == property) {
599             // The property cannot be None for a MULTIPLE request.
600             return false;
601         }
602 
603         boolean conversionSucceeded = false;
604 
605         // First retrieve the list of requested targets.
606         WindowPropertyGetter wpg =
607                 new WindowPropertyGetter(requestor, XAtom.get(property),
608                                          0, MAX_LENGTH, false,
609                                          XConstants.AnyPropertyType);
610         try {
611             wpg.execute();
612 
613             if (wpg.getActualFormat() == 32 && (wpg.getNumberOfItems() % 2) == 0) {
614                 final long count = wpg.getNumberOfItems() / 2;
615                 final long pairsPtr = wpg.getData();
616                 boolean writeBack = false;
617 
618                 for (int i = 0; i < count; i++) {
619                     long target = Native.getLong(pairsPtr, 2 * i);
620                     long prop = Native.getLong(pairsPtr, 2 * i + 1);
621 
622                     if (!convertAndStore(requestor, target, prop)) {
623                         // To report failure, we should replace the
624                         // target atom with 0 in the MULTIPLE property.
625                         Native.putLong(pairsPtr, 2 * i, 0);
626                         writeBack = true;
627                     }
628                 }
629                 if (writeBack) {
630                     XToolkit.awtLock();
631                     try {
632                         XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
633                                                     requestor,
634                                                     property,
635                                                     wpg.getActualType(),
636                                                     wpg.getActualFormat(),
637                                                                 XConstants.PropModeReplace,
638                                                     wpg.getData(),
639                                                     wpg.getNumberOfItems());
640                     } finally {
641                         XToolkit.awtUnlock();
642                     }
643                 }
644                 conversionSucceeded = true;
645             }
646         } finally {
647             wpg.dispose();
648         }
649 
650         return conversionSucceeded;
651     }
652 
handleTargetsRequest(long property, long requestor)653     private boolean handleTargetsRequest(long property, long requestor)
654             throws IllegalStateException
655     {
656         boolean conversionSucceeded = false;
657         // Use a local copy to avoid synchronization.
658         long[] formatsLocal = formats;
659 
660         if (formatsLocal == null) {
661             throw new IllegalStateException("Not an owner.");
662         }
663 
664         long nativeDataPtr = 0;
665 
666         try {
667             final int count = formatsLocal.length;
668             final int dataFormat = 32;
669 
670             if (count > 0) {
671                 nativeDataPtr = Native.allocateLongArray(count);
672                 Native.put(nativeDataPtr, formatsLocal);
673             }
674 
675             conversionSucceeded = true;
676 
677             XToolkit.awtLock();
678             try {
679                 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), requestor,
680                                             property, XAtom.XA_ATOM, dataFormat,
681                                             XConstants.PropModeReplace,
682                                             nativeDataPtr, count);
683             } finally {
684                 XToolkit.awtUnlock();
685             }
686         } finally {
687             if (nativeDataPtr != 0) {
688                 XlibWrapper.unsafe.freeMemory(nativeDataPtr);
689                 nativeDataPtr = 0;
690             }
691         }
692         return conversionSucceeded;
693     }
694 
fireOwnershipChanges(final boolean isOwner)695     private void fireOwnershipChanges(final boolean isOwner) {
696         OwnershipListener l = null;
697         synchronized (stateLock) {
698             l = ownershipListener;
699         }
700         if (null != l) {
701             l.ownershipChanged(isOwner);
702         }
703     }
704 
registerOwershipListener(OwnershipListener l)705     void registerOwershipListener(OwnershipListener l) {
706         synchronized (stateLock) {
707             ownershipListener = l;
708         }
709     }
710 
unregisterOwnershipListener()711     void unregisterOwnershipListener() {
712         synchronized (stateLock) {
713             ownershipListener = null;
714         }
715     }
716 
717     private static class SelectionEventHandler implements XEventDispatcher {
dispatchEvent(XEvent ev)718         public void dispatchEvent(XEvent ev) {
719             switch (ev.get_type()) {
720             case XConstants.SelectionNotify: {
721                 XToolkit.awtLock();
722                 try {
723                     XSelectionEvent xse = ev.get_xselection();
724                     // Ignore the SelectionNotify event if it is not the response to our last request.
725                     if (propertyGetter != null && xse.get_time() == lastRequestServerTime) {
726                         // The property will be None in case of convertion failure.
727                         if (xse.get_property() == selectionPropertyAtom.getAtom()) {
728                             propertyGetter.execute();
729                             propertyGetter = null;
730                         } else if (xse.get_property() == 0) {
731                             propertyGetter.dispose();
732                             propertyGetter = null;
733                         }
734                     }
735                     XToolkit.awtLockNotifyAll();
736                 } finally {
737                     XToolkit.awtUnlock();
738                 }
739                 break;
740             }
741             case XConstants.SelectionRequest: {
742                 XSelectionRequestEvent xsre = ev.get_xselectionrequest();
743                 long atom = xsre.get_selection();
744                 XSelection selection = XSelection.getSelection(XAtom.get(atom));
745 
746                 if (selection != null) {
747                     selection.handleSelectionRequest(xsre);
748                 }
749                 break;
750             }
751             case XConstants.SelectionClear: {
752                 XSelectionClearEvent xsce = ev.get_xselectionclear();
753                 long atom = xsce.get_selection();
754                 XSelection selection = XSelection.getSelection(XAtom.get(atom));
755 
756                 if (selection != null) {
757                     selection.lostOwnership();
758                 }
759 
760                 XToolkit.awtLock();
761                 try {
762                     XToolkit.awtLockNotifyAll();
763                 } finally {
764                     XToolkit.awtUnlock();
765                 }
766                 break;
767             }
768             }
769         }
770     };
771 
772     private static class IncrementalDataProvider implements XEventDispatcher {
773         private final long requestor;
774         private final long property;
775         private final long target;
776         private final int format;
777         private final byte[] data;
778         private int offset = 0;
779 
780         // NOTE: formats other than 8 are not supported.
IncrementalDataProvider(long requestor, long property, long target, int format, byte[] data)781         public IncrementalDataProvider(long requestor, long property,
782                                        long target, int format, byte[] data) {
783             if (format != 8) {
784                 throw new IllegalArgumentException("Unsupported format: " + format);
785             }
786 
787             this.requestor = requestor;
788             this.property = property;
789             this.target = target;
790             this.format = format;
791             this.data = data;
792 
793             XWindowAttributes wattr = new XWindowAttributes();
794             try {
795                 XToolkit.awtLock();
796                 try {
797                     XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), requestor,
798                                                      wattr.pData);
799                     XlibWrapper.XSelectInput(XToolkit.getDisplay(), requestor,
800                                              wattr.get_your_event_mask() |
801                                              XConstants.PropertyChangeMask);
802                 } finally {
803                     XToolkit.awtUnlock();
804                 }
805             } finally {
806                 wattr.dispose();
807             }
808             XToolkit.addEventDispatcher(requestor, this);
809         }
810 
dispatchEvent(XEvent ev)811         public void dispatchEvent(XEvent ev) {
812             switch (ev.get_type()) {
813             case XConstants.PropertyNotify:
814                 XPropertyEvent xpe = ev.get_xproperty();
815                 if (xpe.get_window() == requestor &&
816                     xpe.get_state() == XConstants.PropertyDelete &&
817                     xpe.get_atom() == property) {
818 
819                     int count = data.length - offset;
820                     long nativeDataPtr = 0;
821                     if (count > MAX_PROPERTY_SIZE) {
822                         count = MAX_PROPERTY_SIZE;
823                     }
824 
825                     if (count > 0) {
826                         nativeDataPtr = XlibWrapper.unsafe.allocateMemory(count);
827                         for (int i = 0; i < count; i++) {
828                             Native.putByte(nativeDataPtr+i, data[offset + i]);
829                         }
830                     } else {
831                         assert (count == 0);
832                         // All data has been transferred.
833                         // This zero-length data indicates end of transfer.
834                         XToolkit.removeEventDispatcher(requestor, this);
835                     }
836 
837                     XToolkit.awtLock();
838                     try {
839                         XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
840                                                     requestor, property,
841                                                     target, format,
842                                                     XConstants.PropModeReplace,
843                                                     nativeDataPtr, count);
844                     } finally {
845                         XToolkit.awtUnlock();
846                     }
847                     if (nativeDataPtr != 0) {
848                         XlibWrapper.unsafe.freeMemory(nativeDataPtr);
849                         nativeDataPtr = 0;
850                     }
851 
852                     offset += count;
853                 }
854             }
855         }
856     }
857 
858     private static class IncrementalTransferHandler implements XEventDispatcher {
dispatchEvent(XEvent ev)859         public void dispatchEvent(XEvent ev) {
860             switch (ev.get_type()) {
861             case XConstants.PropertyNotify:
862                 XPropertyEvent xpe = ev.get_xproperty();
863                 if (xpe.get_state() == XConstants.PropertyNewValue &&
864                     xpe.get_atom() == selectionPropertyAtom.getAtom()) {
865                     XToolkit.awtLock();
866                     try {
867                         if (propertyGetter != null) {
868                             propertyGetter.execute();
869                             propertyGetter = null;
870                         }
871                         XToolkit.awtLockNotifyAll();
872                     } finally {
873                         XToolkit.awtUnlock();
874                     }
875                 }
876                 break;
877             }
878         }
879     };
880 }
881